How do I incrementally rotate through an array with Swift? - arrays

Just learning swift and wanted to rotate through an array of colors like so:
class ColorSwitcher
{
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int?
var selectedColor:String{
return self.colors[currIndex!]
}
init(){
currIndex = 0
}
func changeColor()
{
currIndex++ //this doesn't work
}
}
When I try to call the function like so:
var switcher:ColorSwitcher = ColorSwitcher()
switcher.selectedColor // returns red
switcher.changeColor()
switcher.selectedColor // still returns red
The problem is with the changeColor function. The error I get is:
Could not find an overload for '++' that accepts the supplied arguments
What am I doing wrong?

The problem is that currIndex is an optional. I'd suggest refactoring like so:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int = 0
var selectedColor:String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}
if you want to keep it an optional you'll need to do this:
currIndex = currIndex! + 1
but of course that isn't safe, so you should probably do:
if let i = currIndex {
currIndex = i + 1
}
else {
currIndex = 1
}
Also, keep in mind that you don't need to use an optional if you're going to set the value in your init(). The following is fine:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int
init(startIndex: Int) {
currIndex = startIndex
}
var selectedColor:String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}

you can overload the missing ++ operator for optional Int, like e.g. this:
#assignment #postfix func ++(inout x: Int?) -> Int? {
if x != nil {
x = x! + 1
return x
} else {
return nil
}
}
or you can change your class, like e.g. this:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex: Int = 0
var selectedColor: String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}
NOTE: that does not hold any improvement in your class's internal behaviour. it will do the same for you as did in your OP.

Related

Loop through array when state on UIPanGesture is .ended

I would like to know how I can loop through an array and change a name after recognizer.state .ended. When I use forEach method it shows me only the last item from the array.
Array:
let persons = [
Name(name: "Theo", imageName: "theoPhoto"),
Name(name: "Matt", imageName: "mattPhoto"),
Name(name: "Tom", imageName: "tomPhoto")
]
UIPanGestureRecognizer:
#IBAction func handleCard(recognizer: UIPanGestureRecognizer) {
guard let card = recognizer.view else { return }
switch recognizer.state {
case .began:
initialCGRect = card.frame
case .changed:
handleChanged(recognizer, card)
case .ended:
handleEnded(recognizer, card)
default:
return
}
}
fileprivate func handleChanged(_ recognizer: UIPanGestureRecognizer, _ card: UIView) {
let panning = recognizer.translation(in: view)
let degrees : CGFloat = panning.x / 20
let angle = degrees * .pi / 180
let rotationTransformation = CGAffineTransform(rotationAngle: angle)
card.transform = rotationTransformation.translatedBy(x: panning.x, y: panning.y)
}
fileprivate func handleEnded(_ recognizer: UIPanGestureRecognizer, _ card: UIView) {
let transitionDirection: CGFloat = recognizer.translation(in: view).x > 0 ? 1 : -1
let shuldDismissCard = abs(recognizer.translation(in: view).x) > treshold
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
if shuldDismissCard {
card.frame = CGRect(x: 600 * transitionDirection, y: 0, width: card.frame.width, height: card.frame.height)
} else {
card.transform = CGAffineTransform.identity
}
}) { (_) in
// Complete animation, bringing card back
card.transform = CGAffineTransform.identity
}
}
forEach method:
func setupCards() {
persons.forEach { (Person) in
cardView.image = UIImage(named: Person.imageName)
cardLabel.text = Person.name
}
}
If I put it on complete animation it's looping through an array and shows me the last item.
Your setupCards() method is wrong. It will always display the last item because you're updating only a single cardView. Also don't use the Person class name in the loop closure. You should hold a CardView array, and change the data accordingly.
func setupCards() {
var i = 0
persons.forEach { (p) in
let cardView = self.cards[i]
cardView.image = UIImage(named: p.imageName)
cardView.cardLabel.text = p.name
i += 1
}
}

How to store array values depending on a condition?

I have variables of type string, [int], bool
var books:[String] = ["Hobbit","LOTR","Fellowship"]
var chaptersToRead:[[Int]] = [[1],[1,3],[2,3]]
var read:[Bool] = [false,true,true]
I have function display() so that I can see details about all the books individually
func display() -> [[Any]] {
var output = [[Any]]()
for i in 0..<books.count {
output.append([books[i], chaptersToRead[i], read[i]])
}
return output
}
I like to store values of books in two arrays based on condition if chaptersToRead = 1, as follows
var booksAssigned:[String] = ["Hobbit","LOTR"]
var readStatus:[Bool] = [false,true]
I tried to get the above result by doing the following below which is not working. What am I doing wrong?
var booksAssigned:[String] = []
var readStatus:[Bool] = []
for (index, books) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
//I am getting signal SIGABRT error here
booksAssigned = books as! [String]
}
}
for (index, status) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
//I am getting signal SIGABRT error here
readStatus = status as! [Bool]
}
}
Try this:
var booksAssigned:[String] = []
var readStatus:[Bool] = []
for (index, chapters) in chaptersToRead.enumerated() {
if chapters.contains(1) {
booksAssigned.append(books[index])
readStatus.append(read[index])
}
}
print(booksAssigned)
print(readStatus)
Edit: Edited as per #Nirav D's suggestion.
Remove var keyword from if blocks. You have already declared those variables.
var booksAssigned:[Any] = []
var readStatus:[Any] = []
for (index, books) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
booksAssigned = books
}
}
for (index, status) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
readStatus = status
}
}
This works.

Find element in an array of object

I created an array of objects:
var fullMonthlyList = [SimulationMonthly]()
The class here:
class SimulationMonthly {
var monthlyMonthDuration: NSNumber = 0
var monthlyYearDuration: NSNumber = 0
var monthlyFullAmount: NSNumber = 0
var monthlyAmount: Int = 0
init(monthlyMonthDuration: NSNumber, monthlyYearDuration: NSNumber, monthlyFullAmount: NSNumber, monthlyAmount: Int){
self.monthlyMonthDuration = monthlyMonthDuration
self.monthlyYearDuration = monthlyYearDuration
self.monthlyFullAmount = monthlyFullAmount
self.monthlyAmount = monthlyAmount
}
}
I just did append to populate it, now I want to find for example if they're an existing value, for example monthlyAmount equals to "194" by search in the array, how can I do ? I have tried filter and contains but I get errors.
What I've tried:
if self.fullMonthlyList.filter({ $0.monthlyAmount == self.monthlyAmount.intValue }) { ... }
Error:
Cannot invoke 'filter' with an argument list of type '((SimulationMonthly) throws -> Bool)'
You can do:
if let sim = fullMonthlyList.first(where: { $0.monthlyAmount == 194 }) {
// Do something with sim or print that the object exists...
}
This will give you the first element in your array where monthlyAmount equals 194.
If you want all elements with that condition, you can use filter:
let result = fullMonthlyList.filter { $0.monthlyAmount == 194 }
If you don't need the object at all but you just want to know if one exists, then contains would be enough:
let result = fullMonthlyList.contains(where: { $0.monthlyAmount == 194 })
Here's a simple playground example of filtering objects based on matching a property. You should be able to expand it to your situation.
class Item {
var value: Int
init(_ val: Int) {
value = val
}
}
var items = [Item]()
for setting in 0..<5 {
items.append(Item(setting))
}
func select(_ with: Int) -> [Item] {
return items.filter { $0.value == with }
}
let found = select(3)

how to fill <array> struct by another struct

My app in Xcode with swift language programming :
I have a struct like:
struct PageFilter {
var key: Int?
var title: NSString?
}
And then I have the values in:
filters are coming from API and i am saving them to extractedFilter
if let filters = filters {
for filter in filters {
var extractedFilter = PageFilter()
extractedFilter.key = filter["key"].integerValue
extractedFilter.title = filter["title"].stringValue
}
}
I have an array of page filter like :
lazy var availableFilters = Array<PageFilter>()
I want to fill the availableFilters with ExtractedFilter.
******* *i fixed the issue by a loop like this code :
var strFilter : String = ""
for var i = 0; i < self.newFilterList.availableGuildFilters.count; i++ {
let guildFilter = self.newFilterList.availableGuildFilters[i]
if guildFilter.selected {
strFilter += "\(guildFilter.key),"
}
}
thanks to all*
The following Swift 1.2 playground code would do it - I have put in a function to simulate the call to the API
//: Playground - noun: a place where people can play
import Cocoa
struct PageFilter {
var key: Int?
var title: NSString?
}
// this would be replaced by whatever way you get your filters from the API
func getFiltersFromApi() -> [PageFilter]? {
// return nil // uncomment this line to demo the API returning nothing
return [PageFilter(key: 1, title: "one"),
PageFilter(key: 2, title: "two"),
PageFilter(key: 3, title: "three"),
PageFilter(key: nil, title: nil)
]
}
let filters: [PageFilter]? = getFiltersFromApi() // API call, this could return nil
let extractedFilters: [PageFilter]
if let filters = filters {
extractedFilters = filters.map { filter in
PageFilter(key: filter.key, title: filter.title)
}
} else {
extractedFilters = []
}
for filter in extractedFilters {
println("key: \(filter.key), title: \(filter.title)")
}
Alternatively you could have your lazy var like this
var availableFilters: [PageFilter] = {
let filters: [PageFilter]? = getFiltersFromApi() // API call, this could return nil
if let filters = filters {
return filters.map { filter in
PageFilter(key: filter.key, title: filter.title)
}
} else {
return []
}
}()
The code is similar to Leonardo's answer, the main difference being the use of the map function instead of for ... in ...
Try like this:
struct PageFilter {
var key = Int()
var title = String()
}
var filters:[PageFilter]? = []
filters = [PageFilter(key: 1, title: "one"), PageFilter(key: 2, title: "two"), PageFilter(key: 3, title: "three")]
var extractedFilter = Array<PageFilter>()
if let filters = filters {
for filter in filters {
extractedFilter.append(PageFilter(key: filter.key, title: filter.title))
}
}
println(extractedFilter[1].key) // "2"
println(extractedFilter[1].title) // "two"
I fixed the issue by a loop like this:
var strFilter : String = ""
for var i = 0; i < self.newFilterList.availableGuildFilters.count; i++ {
let guildFilter = self.newFilterList.availableGuildFilters[i]
if guildFilter.selected {
strFilter += "\(guildFilter.key),"
}
}

outside array not affected from function appending within

I am working with arrays and I created a function that appends an array from within. However when I print the array, it still appears empty. What gives?
var queriesFinal : [String] = []
func queryValidator(search : String)
{
var letterSet = NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz ")
var numberSet = NSCharacterSet(charactersInString: "1234567890".uppercaseString)
var queriesTwo : [String] = search.lowercaseString.componentsSeparatedByCharactersInSet(letterSet)
for(var x = 0; x < queriesTwo.count; x++)
{
for(var y = 0; y < 10; y++)
{
var str = String(y)
if(queriesTwo[x] == str)
{
var numberStr = String(queriesTwo[x]) + "th"
queriesFinal.append(numberStr)
}
}
}
}
println(queriesFinal)
search = "Matt 8th"
queryValidator(search)
This code can run in playground..
I appreciate any help!
As mentioned by Mike S, you've made a small mistake println should be after your queryValidator, I've also added an optional in case your queryValidator search returns nil, also as mentioned by Zaph you don't need numberSet, so I removed it:
func queryValidator(search : String) -> [String]? {
let queriesTwo:[String] = search.lowercaseString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz "))
var queriesResult:[String] = []
for x in 0...queriesTwo.count-1 {
for y in 0...9 {
if(queriesTwo[x] == String(y)) {
queriesResult.append(String(queriesTwo[x]) + "th")
}
}
}
return queriesResult.count > 0 ? queriesResult : nil
}
var search = "Matt 8 less7"
if let queriesFinal = queryValidator(search) {
println(queriesFinal)
} else {
println("no result")
}
An alternative approach with Regular Expressions:
func queryValidator(search: String) -> [String] {
var queriesFinal:[String] = []
var nsSearch: NSString = search
let pattern = "(\\d+)"
var regEx = NSRegularExpression(pattern:pattern, options:nil, error:nil)
regEx?.enumerateMatchesInString(nsSearch, options:nil, range:NSMakeRange(0, nsSearch.length), usingBlock: { (result, flags, stop) in
let found = nsSearch.substringWithRange(result.range)
queriesFinal.append("\(found)th")
})
return queriesFinal
}
var result = queryValidator(search)
println("result: \(result)")
Output:
result: [8th, 7th, 6th]
For information on regular expressions see: Regular Expressions

Resources