I am creating a custom iOS keyboard and am trying to create a function that will use a JSON dict to see what symbol to use.
Here is my function:
func switchSymbols(option: Int) {
for view in self.view.subviews {
if let button = view as? UIButton {
let buttonTag = button.tag
if (buttonTag > 0 && buttonTag < 100) {
if let text = symbolsDict?[buttonTag][option].stringValue {
print(symbolsDict)
button.setTitle(text, for: .normal)
}
}
}
}
}
And here is the factory function to create the lookup dict:
func symbolsDictFactory() -> JSON {
var dict:JSON = [:]
dict[1][0] = "1"
dict[1][1] = "["
dict[2][0] = "2"
dict[2][1] = "]"
print(dict)
return dict
}
Unfortunately, this second function produces a blank JSON array.
Little lost. Any help appreciated. Thanks in advance.
I would recommend not using JSON for data and models within your application. Your internal models should be strongly typed.
In fact, JSON is really of type [String: Any] which is not what you need since both the button tag and option values are integers. What you describe in your code is [Int: [Int: String]]. There is nothing wrong with creating a function to return your symbol lookup table. However since the table is constant, it may be simpler to just make it a property of your containing class like this.
class View: UIViewController {
private let titleForButton = [
1: [0: "1", 1: "["],
2: [0: "2", 1: "]"],
// ...
99: [0: "😊", 1: "😀"]
]
func switchSymbols(option: Int) {
for view in self.view.subviews {
if let button = view as? UIButton {
if let title = titleForButton[button.tag]?[option] {
button.setTitle(title, for: .normal)
}
}
}
}
}
Also, the bounds checking that you had for button.tag being within [1..<100] is unnecessary since the return value for the dictionary lookup is optional anyways and will not set a title for a button tag out of range.
Related
This question already has answers here:
Swift: Test class type in switch statement
(4 answers)
Closed 1 year ago.
I am showing a tableview with the list generated using the following code. The UI is properly populated. However on did select method, the call is not matched inside the generic case.
#objc public protocol ListProtocol {}
class EmptyListProtocol:ListProtocol {
let message:String
init(message:String){
self.message = message
}
}
class ListItemStrategy<T>: ListProtocol{
let object:T
init(listitem:T) {
self.object = listitem
}
}
struct CartInfo {
let card_id : String
let productname: String
let purchasedate:String
}
struct ProductOnSale {
let product_id:String
let product_name:String
let store_id:String
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = self.tablelist[indexPath.row]
switch item {
case is ListItemStrategy<Any>:
print("didtap")
case is EmptyListProtocol: break
default:
break
}
}
var table list : [ListProtocol] = []
I am not able to detect in particular, if a cart is selected or new purchase product is selected.
When checking the type you need to understand that SomeGeneric<Any> is a specific type and that you can't use the protocol Any here as something that represents any implementation of the generic type.
This means your switch would need to be something like
switch item {
case is ListItemStrategy<ProductOnSale>:
...
case is ListItemStrategy<CartInfo>:
...
case ...
}
I assume you want to access the specific objects which mean you need to type case item so it might be preferable to skip the switch and use if let to cast directly
let item = self.tablelist[indexPath.row]
if let cartInfo = item as? ListItemStrategy<CartInfo> {
...
} else if let productOnSale = item as? ListItemStrategy<ProductOnSale> {
...
} else if let ... {
...
}
Here is a simple example
let tableList : [ListProtocol] = [
ListItemStrategy(listitem: ProductOnSale(product_id: "product1", product_name: "", store_id: "")),
ListItemStrategy(listitem: CartInfo(card_id: "card1", productname: "", purchasedate: "")),
ListItemStrategy(listitem: ProductOnSale(product_id: "product2", product_name: "", store_id: "")),
ListItemStrategy(listitem: EmptyListProtocol(message: "Empty"))
]
for item in tableList {
if let cartInfo = item as? ListItemStrategy<CartInfo> {
print(cartInfo.object.card_id)
} else if let productOnSale = item as? ListItemStrategy<ProductOnSale> {
print(productOnSale.object.product_id)
} else if let emptyMessage = item as? ListItemStrategy<EmptyListProtocol> {
print(emptyMessage.object.message)
}
}
I'm new to Swift. I managed to build an app which almost works, but I cannot get the last steps right. I would appreciate any help!
I wrote a code which displays a user-defined number of UILabels. In those labels, the contents of a [String] is displayed. Under the labels, there is the same number of UITextFields. The user should fill out these text fields, press the button and then see if what he filled out matches the labels.
All the labels, the text fields, and the button are made completely programmatically, so without using the storyboard. In the viewDidLoad there is all the code and this line:
myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
Right under viewDidLoad there is this function which I found on this forum and I changed it a bit:
#objc func buttonAction(sender: UIButton!) -> [String] {
var a = 0
var userInput: [String] = Array()
while a < 4 {
if let myTextField = self.view.viewWithTag(a) as? UITextField {
let tekstInput = myTextField.text
userInput.insert(tekstInput!, at:a-1)
}
a = a + 1
}
return userInput
}
My problems:
The while-loop in the function shouldn't have 4 as the maximum, but a user-defined variable. But if I try to change function so that it expects that variable as an input, I get error messages in the myButton.addTarget line.
How do I read out the return in the viewdidload to add there the code to compare the user input with the original [String]?
You should consider the source of the user-defined input if you want to answer your question.
For instance, if you are willing to add an extra UITextField to retrieve your user input, then all you have to do is extract the value from that text field within your buttonAction(sender:) method and use it there. This translates roughly to the following
#objc func buttonAction(_ sender: UIButton) {
var a = 0
var userInput: [String] = Array()
guard let upperLimit = self.userInputTextField.text as? Int else {
return
}
while a < upperLimit {
if let myTextField = self.view.viewWithTag(a) as? UITextField {
let tekstInput = myTextField.text
userInput.insert(tekstInput!, at: a-1)
}
a = a + 1
}
}
Note that self.userInputTextField is the extra text field you should add in order to retrieve your user-defined input.
I have problem with selectedRows() in SelectableSection.
Using Xcode 8, Swift 3, Eureka 2.0.0-beta.1.
func viewDidLoad() {
let branch_section = SelectableSection<ImageCheckRow<String>>("Branches", selectionType: .multipleSelection)
branch_section.tag = "branch_section"
for branch in branchList {
let branchStr = String(branch.id)
branch_section <<< ImageCheckRow<String>(branch.name){ row in
row.title = branch.name
row.selectableValue = branchStr
row.value = nil
}
}
}
#IBAction func saveFilter(_ sender: AnyObject) {
let branch_section = self.form.sectionBy(tag: "branch_section") as? SelectableSection<ImageCheckRow<String>>
invoiceParams["branches"] = branch_section!.selectedRows().map({$0.value!})
}
now i have problem with this line invoiceParams["branches"] = branch_section!.selectedRows().map({$0.value!})
map' produces '[T]', not the expected contextual result type
'AnyObject?'
What is problem here? This worked with previous versions on swift 2.3.
As far as I can see from SelectableSection.swift selectedRows returns an array of SelectableRow items:
public func selectedRows() -> [SelectableRow] {
return filter({ (row: BaseRow) -> Bool in
row is SelectableRow && row.baseValue != nil
}).map({ $0 as! SelectableRow})
}
So the map function also returns an array.
Your invoiceParams seems to be a dictionary that expects AnyObject? as value.
You can try to change the declaration of invoiceParams to something like var invoiceParams: [String: [Any]] = [:].
Since I don't know Eureka this is just a guess. But I hope it still helps a little.
I'm trying to figure out how the array works with Swift. I understand that you use let to create an immutable array and var to create a mutable array. Yet, Swift's array is not quite the same as Objective's NSArray and NSMutableArray. I get that.
In the example that follows, I create a mutable array with one element. Actually, I want to start with no element. But if I do, I won't be able to add a new string to it. If I start with one element, then I won't be able to add a new string to it after the original element is removed. So what am I doing wrong?
Thank you
EDIT
class ViewController: UIViewController {
let textCellIdentifier = "TextCell"
var myArray:[String] = ["GGG"] // or var myArray:[String] = []
#IBAction func add1Tapped(sender:AnyObject) {
let index = tableView1.indexPathForSelectedRow;
let selectedRow = index()?.row
if selectedRow < 0 {
return
} else {
let txt = nameField1.text
myArray.append(txt)
tableView1.reloadData()
}
}
func tableView(tableView: UITableView,numberOfRowsInSection section:Int) -> Int {
return myArray.count
}
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell=UITableViewCell(style: UITableViewCellStyle.Subtitle,reuseIdentifier:textCellIdentifier)
let row = indexPath.row
cell.textLabel!.text = myArray[row]
return cell
}
}
all your needs should work as expected:
// create an empty array of strings
var myArray: [String] = []
// add two elements
myArray.append("the first element")
myArray.append("the second element")
// remove both elements
myArray.removeAll()
// add another element
myArray.append("the third but now first element")
myArray.count
EDIT
try and change your add method like this:
#IBAction func add1Tapped(sender:AnyObject) {
if let _ = tableView1.indexPathForSelectedRow, txt = nameField1.text {
print("will append \(txt) to myArray")
myArray.append(txt)
tableView1.reloadData()
}
}
I'm playing around with custom cells.
With the great help from the stackoverflow community i've been able to put some code together. I'm able to fetch array values from a text string into a custom cell uilabel and uibutton, but the issue is - the fetched result is always the last object in the array.
Here is the code
func setUpQuestion()
{
// setting variables
var Question: String?
var option1: String?
// using a text string with custom separators
let text = ">>Here is the grocery question\n>>and another one\n--Apples\n-
-Oranges\n[pickApples]pickOranges\n[pickApples2]"
// splitting this string into four different arrays depending on the separator
let lines = split(text) { $0 == "\n" }
for line in lines {
if line.hasPrefix(">>") {
Question = line.substringFromIndex(advance(line.startIndex, 2))
} else if line.hasPrefix("[") {
if let index = line.rangeOfString("]")?.startIndex {
option1 = line.substringWithRange(Range<String.Index>(
start: advance(line.startIndex, 1), end: index))
}
}
}
// creating variables for appending the values - here I'm using a custom class called QuestionMark created in a separate .swift file
var question1 = QuestionMark(Question: Question!, option:option1!)
var question2 = QuestionMark(Question: Question!, option:option1!)
// appending the values into uilabel and uibutton in the custom cell
arrayOfQuestions.append(question1)
arrayOfQuestions.append(question2)
}
// regular tableView protocol functions
func tableView(tableView: UITableView, numberOfRowsInSection
section: Int) ->Int
{
return arrayOfQuestions.count
}
func updateCount(){
if let list = mainTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
rowsCount.text = String(list.count)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomCellForTableViewTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomCellForTableViewTableViewCell
// the SetCell function i'm using here was created in a separate .swift file
let quest = arrayOfQuestions[indexPath.row]
cell.setCell(quest.Questme!, optionone: quest.optionize!)
cell.optionOne.backgroundColor = UIColor.redColor()
return cell
}
Here are the additional codes i'm using for the class and setCell function
class QuestionMark
{
var Questme: String?
var optionize: String?
init(Question: String, option: String)
{
self.Questme = Question
self.optionize = option
}
// separate swift file
func setCell(Question: String, optionone: String)
{
self.mainText.text = Question
self.optionOne.setTitle(optionone, forState:UIControlState.Normal)
}
As a result in both cells i'm getting the last object from the text string and it looks like this
And another one - PickApples2
And another one - PickApples2
How do i start appending cells from the first array value and then move forward to second,third,fourth ?
Any ideas are greatly appreciated.
Thank you.
First of all, the syntax of the text to parse is pretty complicated ;-) …
Second of all, the problem to get always the last object is that you create the array of questions after the repeat loop. At that moment the variables question and option contain always the last found string.
Here a solution:
After getting a question a new object QuestionMark is created and appended to the array (without the optionize property)
After getting an option the appropriate QuestionMark object is fetched from the array by an index counter, the property optionize is set and the counter is increased.
Two notes:
Variable names should always start with a lowercase letter. Even the syntax highlighter of StackOverflow follows that naming convention.
In my solution all variables are non-optionals.
class QuestionMark
{
var questme: String
var optionize: String
init(question: String, option: String = "")
{
self.questme = question
self.optionize = option
}
...
var arrayOfQuestions = [QuestionMark]()
func setupQuestion() {
let text = ">>Here is the grocery question\n>>and another one\n--Apples\n--Oranges\n[pickApples]pickOranges\n[pickApples2]"
// splitting this string into four different arrays depending on the separator
var counter = 0
var question = ""
var option = ""
let lines = split(text) { $0 == "\n" }
for line in lines {
if line.hasPrefix(">>") {
question = line.substringFromIndex(advance(line.startIndex, 2))
let questionMark = QuestionMark(question: question)
arrayOfQuestions.append(questionMark)
} else if line.hasPrefix("[") {
if let index = line.rangeOfString("]")?.startIndex {
option = line.substringWithRange(Range<String.Index>(
start: advance(line.startIndex, 1), end: index))
let questionMark = arrayOfQuestions[counter]
questionMark.optionize = option
counter++
}
}
}
}