getting 3 pickerviews to add together to give me a number - uikit

Hi I'm learning swift very green and unfortunately i have been learning via reading the swift.org files and random youtube but this is the problem I either don't know what questions to search for or what i need to do to fix the issue.
I am trying to calculate the GCS Glasgow Coma Scale using 3 pickerviews and a textField for the ending total. I have what i think is a working pickerview for all three fields which is eyes, verbal, motor. all placed into different arrays. how do i get the picker selection to give me data that i can then do a if else logic or something to add the 3 answers together. (they are in string but i need a int)
any help will be much appreciated and if i am completely wrong please help me correct my issues
class TraumaViewController: UIViewController {
#IBOutlet weak var eyesField: UITextField!
#IBOutlet weak var verbalField: UITextField!
#IBOutlet weak var motorField: UITextField!
#IBOutlet weak var TotalGCSfield: UITextField!
let eyes = ["Open - 4", "To Sound - 3", "To Pain - 2", "none - 1"]
let verbal = ["Oriented - 5", "Confused - 4", "Inapproriate - 3", "Incomprehensible - 2", "None - 1"]
let motor = ["Obeys - 6", "Moves - 5", "Withdrawals - 4", "Decorticate - 3", "Decerebate - 2", "None - 1"]
var eyespickerview = UIPickerView()
var verbalpickerview = UIPickerView()
var motorpickerview = UIPickerView()
override func viewDidLoad() {
super.viewDidLoad()
for item in eyes {
print (item)
}
// pickerview.delegate = self
// pickerview.dataSource = self
eyesField.inputView = eyespickerview
verbalField.inputView = verbalpickerview
motorField.inputView = motorpickerview
eyespickerview.delegate = self
eyespickerview.dataSource = self
verbalpickerview.delegate = self
verbalpickerview.dataSource = self
motorpickerview.delegate = self
motorpickerview.dataSource = self
eyespickerview.tag = 1
verbalpickerview.tag = 2
motorpickerview.tag = 3
}
}
extension TraumaViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch pickerView.tag {
case 1:
return eyes.count
case 2:
return verbal.count
case 3:
return motor.count
default:
return 1
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch pickerView.tag {
case 1:
return eyes[row]
case 2:
return verbal[row]
case 3:
return motor[row]
default:
return "data not found"
}
func eyesConvert() {
if let eyes = Double(eyesField.text!) {
print (eyes)
}
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch pickerView.tag {
case 1:
eyesField.text = eyes[row]
eyesField.resignFirstResponder()
case 2:
verbalField.text = verbal[row]
verbalField.resignFirstResponder()
case 3:
motorField.text = motor[row]
motorField.resignFirstResponder()
default:
return
}
}

Related

How to update the NSProgressIndicator?

I have problem with NSProgress. The problem is that NSProgressIndicator is not updating during the process and showing only a small completed portion at the end of process. The localizedDescription is also showing only at the end of the process, but as 100% completed.
So, I have a class with one method findRepeatsWithProgressReporting using NSProgress
class TestProgress: NSObject, ProgressReporting
{
let progress: Progress
override init()
{
progress = Progress()
super.init()
}
func findRepeatsWithProgressReporting(stringToSearch: String, minimalLength: Int, maximalLength: Int) -> [String]
{
var arrayOfRepeats = [String]()
progress.totalUnitCount = Int64((minimalLength...maximalLength).count)
for i in minimalLength...maximalLength
{
let arrayOfStrings = stringToSearch.chopString(stringOut: stringToSearch, length: i)
let arrayOfUniqueStrings = Array(Set(arrayOfStrings))
for each in arrayOfUniqueStrings
{
let arrayOfNSRanges = stringToSearch.searchForNSRangesOfStringInString(stringOut: stringToSearch, stringIn: each)
var positions = String()
if arrayOfNSRanges.count > 1
{
for each1 in arrayOfNSRanges
{
let repeatStart = String(each1.location + 1)
let repeatEnd = String(each1.location + each1.length)
positions += "(" + repeatStart + "-" + repeatEnd + ")"
}
let stringToShow = each + " " + positions
arrayOfRepeats.append(stringToShow)
}
}
progress.completedUnitCount += 1
}
return arrayOfRepeats
}
}
Then, in myVewContrloler I have parentProgress repeatsProgress having totalUnitCount: 10 and have added the task of the method findRepeatsWithProgressReporting as childProgress to the parentProgress repeatsProgress using repeatsProgress.becomeCurrent(withPendingUnitCount: 10).
private var progressObservationContext = 0
class myVewContrloler: NSViewController
{
...
var testProgress = TestProgress ()
var repeatsProgress = Progress()
#IBOutlet weak var repeatsSearchProgressBar: NSProgressIndicator!
#IBOutlet weak var repeatsPercentText: NSTextField!
#IBOutlet weak var minimalLength: NSTextField!
#IBOutlet weak var maximalLength: NSTextField!
#IBOutlet var foundRepeats: NSTextView!
#IBAction func actionFindRepeats(_ sender: AnyObject)
{
repeatsProgress = Progress(totalUnitCount: 10)
let options : NSKeyValueObservingOptions = [.new, .old, .initial, .prior]
repeatsProgress.addObserver(self, forKeyPath: "fractionCompleted", options: options, context: &progressObservationContext)
repeatsProgress.addObserver(self, forKeyPath: "localizedDescription", options: options, context: &progressObservationContext)
var arrayOfRepeats = [String]()
repeatsProgress.becomeCurrent(withPendingUnitCount: 10)
arrayOfRepeats = testProgress.findRepeatsWithProgressReporting(stringToSearch: stringToSearch, minimalLength: minimalLength.integerValue, maximalLength: maximalLength.integerValue)
...
repeatsProgress.removeObserver(self, forKeyPath: "fractionCompleted")
repeatsProgress.removeObserver(self, forKeyPath: "localizedDescription")
repeatsProgress.resignCurrent()
}
}
The last part is for KVO :
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
guard context == &progressObservationContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath == "fractionCompleted"
{
OperationQueue.main.addOperation{
let progress = object as! Progress
self.repeatsSearchProgressBar.doubleValue = progress.fractionCompleted
self.repeatsPercentText.stringValue = progress.localizedDescription
}
}
}
I have added
print("Observed Something")
inside of the
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{ ...
and what I see is two times printing the "Observed Something"immediately after start and six times at the end, no printing in between (as it expected to be for the updating process). What can be the reason ?
This seems like a concurrency problem. Since func actionFindRepeats(_ sender: AnyObject) is running in the main thread, it's concurring with the UI updates, which affects the NSProgressIndicator directly.
See the last example of that answer for more details about that:
https://stackoverflow.com/a/35810608/4370893
You can try adding all the content of your actionFindRepeats function into that block and see if it works:
DispatchQueue.global().async {
// qos' default value is ´DispatchQoS.QoSClass.default`
}
Reference for that block:
https://stackoverflow.com/a/37806522/4370893

Set identifier of table cell view programmatically

I have table view like this (in a mac cocoa application):
In the leftmost panel you can see that I have set the identifier of the Table Cell View to "1". That's fine if you just have 2 columns, once the number goes up, this approach will become cumbersome. Can I do this programmatically?
Here is an example:
import Cocoa
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
private var dataModel = DataModel()
private var answer = 0
private var keyData: (Int, [Int]) = (0, []) {
didSet {
tbl.reloadData()
}
}
#IBOutlet weak var questionIndex: NSTextField!
#IBOutlet weak var tbl: NSTableView!
#IBAction func replay(_ sender: Any) {
dataModel = DataModel()
questionIndex.stringValue = "0:"
answer = 0
updateModel()
}
#IBAction func forward(_ sender: NSButton) {
if sender.tag == 1 {
answer += keyData.0
}
updateModel()
}
func updateModel() {
let group = dataModel.nextGroup()
if let g = group {
self.keyData = g
let s = questionIndex.stringValue
questionIndex.stringValue = String(Int(String(s.characters.dropLast()))! + 1) + ":"
return
}
let alert = NSAlert()
alert.messageText = "You did have \(answer) on your mind, didn't you?"
alert.runModal()
}
override func viewDidLoad() {
super.viewDidLoad()
for (n, col) in tbl.tableColumns.enumerated() {
col.identifier = String(n)
}
updateModel()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return keyData.1.count / 8 + 1
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let colId = tableColumn!.identifier
let colIndex = Int(colId)!
let index = (row * 8) + colIndex
let cell = tbl.make(withIdentifier: colId, owner: self) as! NSTableCellView
if 0 <= index && index < keyData.1.count {
cell.textField!.integerValue = keyData.1[index]
} else {
cell.textField!.stringValue = ""
}
return cell
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I have assigned the cell identifiers by hand, and made them identical the corresponding column index, so as to creating a mapping between the cell id and the 2D array (which is the underlying data model) column index. The app is running fine, I just don't like assigning these IDs by click-and-point.
The full project can be found here: https://github.com/kindlychung/MysteriousNum
Create custom cell and add init to it using following lines.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
and register this cell class as.
self.tableView.register(CustomCell.self, forCellReuseIdentifier: "customCell")
also dequeueReusableCell using same cell like:
tableView.dequeueReusableCell(withIdentifier: "customCell",for: indexPath) as! CustomCell

swift Detect the correct textfield to add an attribute

i´m writing an input text quiz app, and i have an array of Int where i can store if answer was correct or not with 0 or 1 and also i have 3 textfields where i write the answers, and i want to change the textfields back ground color to red or green depending on the answers variable ,, if index variable its 1 change color to green and if is 0 change color to red.. this is what i have
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var textField3: UITextField!
//change int to 1 if answer was correct (3, ea for each textfield)
var answers = [0,0,0]
#IBAction func button(sender: AnyObject) {
for (index, answer) in goodAnswers.enumerate() {
print (answer)
if answer != 0 {
print ("ok")
} else {
print("not ok")
}
}
}
thanks !
You need something like this:
var goodAnswers = ["One", "Two", "Three"]
var textFields:[Int:UITextField]!
override func viewDidLoad() {
super.viewDidLoad()
self.textFields = [0:textField, 1:textField2]
}
#IBAction func btnClick(sender: AnyObject) {
for(index, answer) in goodAnswers.enumerate() {
if textFields[index]?.text == answer {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
UPDATE:
If you want to have another good answers simply change your code to like this:
Declare new property:
var collectionOfGoodAnswers: [Int : [String]]!
and in viewDidLoad() method:
self.collectionOfGoodAnswers = [0: ["Hello", "World"],
1: ["Welcome", "Friend"]]
and then implement the click action:
#IBAction func btnClick(sender: AnyObject) {
for(index, _) in collectionOfGoodAnswers.enumerate() {
guard let goodAnswer = collectionOfGoodAnswers[index] else { return }
guard let answer = textFields[index] else { return }
guard let text = answer.text else { return }
if goodAnswer.contains(text) {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
hope it helps.

Sample TableView not print the array Swift OSX

I have the next code in my application but only print the next image,i don't know why.Someone who can help me solve this error.I think that the error is in the Dictionary.
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var viewTable: NSTableView!
func getDataArray () -> NSArray{
var dataArray:[NSDictionary] = [["FirstName": "Debasis", "LastName": "Das"],
["FirstName": "Nishant", "LastName": "Singh"],
["FirstName": "John", "LastName": "Doe"],
["FirstName": "Jane", "LastName": "Doe"],
["FirstName": "Mary", "LastName": "Jane"]];
return dataArray;
}
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
let numberOfRows:Int = getDataArray().count
return numberOfRows
}
func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!
{
var newString: (AnyObject?) = getDataArray().objectAtIndex(row).objectForKey(tableColumn.identifier)
println(newString)
return newString;
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
}
}
}
Maybe it's silly but it's good to know how to do, I hope it will be useful.Finally my code works, the only thing he needed was set as data source.
Have you set the Identifier for the cells in the storyboard? If so then just get the row and with the identifier you can get the "FirstName" or "LastName", wich you return
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject?
{
var columnIdentifier = tableColumn!.identifier
var newString: (AnyObject?)
newString = getDataArray().objectAtIndex(row).valueForKey(columnIdentifier)
println(newString)
return newString
}
If you have further questions how to work with cell based tableViews: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TableView/PopulatingCellTables/PopulatingCellTables.html
Edit:
you should set the tableView as Cell Based.
And i think you forget to set ViewController as dataSource and delegete for the tableview in the Storyboard

How to use two UIPickerViews in one view controller?

I have two UIPickerControllers in one view controller. I can get one to work, but when I add a second, my app crashes. Here is the code I use for one picker view:
import UIKit
class RegisterJobPosition: UIViewController, UIPickerViewDelegate {
#IBOutlet weak var positionLabel: UILabel!
var position = ["Lifeguard", "Instructor", "Supervisor"]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfComponentsInPickerView(PickerView: UIPickerView!) -> Int
{
return 1
}
func pickerView(pickerView: UIPickerView!, numberOfRowsInComponent component: Int) -> Int
{
return position.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!
{
return position[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
positionLabel.text = position[row]
}
}
Now, how can I get a second picker to work? Say my second picker view is called location (the other one is called position). I tried duplicating the code within the picker view methods for location but it doesn't work.
Here is my solution:
in the storyboard, add two UIPickerView instances to your view
set the first picker's tag as 1 and set 2 for the second picker under the "Attributes Inspector"
control + drag from each picker to the top yellow view controller icon and choose dataSource. Repeat the same choosing delegate
add UIPickerViewDataSource and UIPickerViewDelegate to your view controller:
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
in your view controller class, create empty arrays for the pickers:
var picker1Options = []
var picker2Options = []
In viewDidLoad(), populate the arrays with your content:
picker1Options = ["Option 1","Option 2","Option 3","Option 4","Option 5"]
picker2Options = ["Item 1","Item 2","Item 3","Item 4","Item 5"]
implement the delegate and data source methods:
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView.tag == 1 {
return picker1Options.count
} else {
return picker2Options.count
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
if pickerView.tag == 1 {
return "\(picker1Options[row])"
} else {
return "\(picker2Options[row])"
}
}
Based on the information I have in the question, I'd say that you need to set up the data source & delegate methods to handle the ability to distinguish between which picker instance is calling them.
Using the tag property on the picker view is one strategy.
There should be some if/else or switch statements in the methods that have varying logic depending on whether it's the location or the position picker that's being referenced.
I found this to work.
class SecondViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var textbox1: UILabel!
#IBOutlet weak var textbox2: UILabel!
#IBOutlet weak var dropdown1: UIPickerView!
#IBOutlet weak var dropdown2: UIPickerView!
var age = ["10-20", "20-30", "30-40"]
var Gender = ["Male", "Female"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
var countrows : Int = age.count
if pickerView == dropdown2 {
countrows = self.Gender.count
}
return countrows
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == dropdown1 {
let titleRow = age[row]
return titleRow
} else if pickerView == dropdown2 {
let titleRow = Gender[row]
return titleRow
}
return ""
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == dropdown1 {
self.textbox1.text = self.age[row]
} else if pickerView == dropdown2 {
self.textbox2.text = self.Gender[row]
}
}
}
My background is in Android but my answer is very OOP. I would suggest creating different classes to implement the DataSource and Delegate like this:
class PositionDataSourceDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
var position = ["Lifeguard", "Instructor", "Supervisor"]
var selectedPosition : String?
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return position.count
}
func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
return position[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedPosition = position[row]
}
}
and then another one for the Location:
class LocationDataSourceDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
var location = ["Up", "Down", "Everywhere"]
var selectedLocation : String?
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return location.count
}
func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
return location[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedLocation = location[row]
}
}
then in your RegisterJobPosition you need to create an instance of each:
let positionDSD = PositionDataSourceDelegate()
let locationDSD = LocationDataSourceDelegate()
and assign them to the pickers like this:
positionPicker.dataSource = positionDSD
positionPicker.delegate = positionDSD
locationPicker.dataSource = locationDSD
locationPicker.delegate = locationDSD
and you can access the selected position and location using:
positionDSD.selectedPosition
locationDSD.selectedLocation
Hope this helps you and others and I'm also hoping for some constructive comments of why this is not "swifty"
I think the biggest issue and different to Java is that Java easily allow for attributes to be passed through the constructor. e.g. you could declare class LocationDataSourceDelegate as generic and call it genericDataSourceDelegate and make the constructor accept and Array public genericDataSourceDelegate (String data[]) and be able to make one class where could just simply create objects of. You just instantiate it and pass location the constructor like genericDataSourceDelegate (location)
The problem with your model you will have to create as many delegate classes in one program which is a strain to your compiler.
You should NEVER use tags!!!!!
The best way for me was:
Create an enum, for example MyPickerViewType
enum MyPickerViewType {
case first
case second
}
Create CustomPickerView class that conforms to UIPickerView and add type parameter
class CustomPickerView: UIPickerView {
let type: MyPickerViewType
init(type: MyPickerViewType) {
self.type = type
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")
}
}
Initialise custom pickerViews in UIViewController
private lazy var firstPickerView: TimeLogCardPickerView = {
let pickerView = TimeLogCardPickerView(type: .first)
pickerView.delegate = self
pickerView.dataSource = self
return pickerView
}()
private lazy var secondPickerView: TimeLogCardPickerView = {
let pickerView = TimeLogCardPickerView(type: .second)
pickerView.delegate = self
pickerView.dataSource = self
return pickerView
}()
Usage
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
let customPickerView = pickerView as? CustomPickerView
switch customPickerView?.type {
case .first:
return 2
case .second:
return 5
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let customPickerView = pickerView as? CustomPickerView
switch customPickerView?.type {
case .first:
return firstTitleArray[row]
case .second:
return secondTitleArray[row]
}
}
}

Resources