Cannot check array range in swift - arrays

I have different arrays, they store a different number of elements, at the click of a button the index of the array is increased by 1 and the element is added to another array, I did an array range check, but it does not work. what could be the problem
xCode console: Fatal error: Index out of range: file
class ViewController: UIViewController {
var currentIndex = 0
var arrayIndex = -1
var arrayWords: [Words]? {
didSet {
// wordTextLabel.text = arrayWords?[0].arrayWords?[currentIndex].name
// wordImage.image = arrayWords?[0].arrayWords?[currentIndex].image
// wordArray.append((arrayWords?[0].arrayWords?[currentIndex])!)
}
}
var testArray = ["Hello","Energy","Disk","Duck","Wafles"]
var wordArray: [String] = [] //[ArrayWords] = []
lazy var wordTextLabel: UILabel = {
let label = UILabel()
label.text = testArray[currentIndex]//arrayWords?[0].arrayWords?[currentIndex].name
label.textColor = .white
label.font = UIFont(name: "OpenSans-ExtraBold", size: 20)
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.isUserInteractionEnabled = true
return label
}()
lazy var wordImage: UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFit
image.tintColor = .white
image.translatesAutoresizingMaskIntoConstraints = false
return image
}()
lazy var button1: UIButton = {
let button = UIButton()
button.backgroundColor = .white
button.setTitle("Good", for: .normal)
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.layer.cornerRadius = 40
button.clipsToBounds = true
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
lazy var button2: UIButton = {
let button = UIButton()
button.backgroundColor = .white
button.setTitle("No", for: .normal)
button.setTitleColor(.black, for: .normal)
button.layer.cornerRadius = 40
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
func setupView() {
view.setGradient(colorOne: Color.purpleColor, colorTwo: Color.pinkColor,
startPoint: CGPoint(x: 1.0, y: 0.1), endPoint: CGPoint(x: 0.1, y: 1.0))
view.addSubview(wordTextLabel)
//wordTextLabel.addSubview(wordImage)
//view.addSubview(wordImage)
view.addSubview(button1)
view.addSubview(button2)
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-120-[v0]-120-|", metrics: nil, views: ["v0": wordTextLabel]))
button1.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -40).isActive = true
button1.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true
button1.heightAnchor.constraint(equalToConstant: 80).isActive = true
button1.widthAnchor.constraint(equalToConstant: 80).isActive = true
button2.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -40).isActive = true
button2.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true
button2.heightAnchor.constraint(equalToConstant: 80).isActive = true
button2.widthAnchor.constraint(equalToConstant: 80).isActive = true
// wordArray.append((arrayWords?[0].arrayWords?[currentIndex])!)
}
#objc func buttonAction(sender: Any!) {
print("Button tapped")
action()
}
func action() {
if currentIndex < testArray.count/*arrayWords?[0].arrayWords?.count*/ { //!= 2 // !=
currentIndex += 1
arrayIndex += 1
wordTextLabel.text = testArray[currentIndex]//arrayWords?[0].arrayWords?[currentIndex].name
wordArray.append(testArray[arrayIndex])//((arrayWords?[0].arrayWords?[arrayIndex])!)
} else if arrayIndex != 3 {
arrayIndex += 1
wordArray.append(testArray[arrayIndex])//((arrayWords?[0].arrayWords?[arrayIndex])!)
}
}
}

Change action() as below and it will work fine. There is no need to maintain two index variables and without the second variable you don't need the else if... either
func action() {
if currentIndex < testArray.count {
wordTextLabel.text = testArray[currentIndex]
wordArray.append(testArray[currentIndex])
}
currentIndex += 1
}

Related

how to transfer part of characters below others in swift

enter image description here> I make the word game, the problem is this. When the word is big, the
cells come out of the screen, I need these cells to move below the
other cells and be centered. and the cells should have a fixed width =
20. that's all in my application Help implement.
p.s. I tried to add elements to the stackview, but the space between the words did not appear in the stackview
1. TileView.swift
import UIKit
protocol TileDragDelegateProtocol {
func tileView(tileView: TileView, didDragToPoint: CGPoint)
}
class TileView: UIImageView {
var letter: Character?
var isMatch = false
private var xOffset: CGFloat = 0.0
private var yOffset: CGFloat = 0.0
var dragDelegate: TileDragDelegateProtocol?
init(letter: Character, sideLength: CGFloat) {
self.letter = letter
//let scale = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
// super.init(frame: scale)
// let scale = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
let image = UIImage(named: "tile")
super.init(image: image)
let scale = sideLength / (image?.size.width)!
self.frame = CGRect(x: 0, y: 0, width: (image?.size.width)! * scale, height: (image?.size.height)! * scale)
// self.frame = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
//self.layer.cornerRadius = 23
//self.clipsToBounds = true
let letterLabel = UILabel(frame: self.bounds)
letterLabel.textAlignment = .center
letterLabel.textColor = .white
letterLabel.backgroundColor = .clear
letterLabel.text = String(letter).uppercased()
letterLabel.font = .boldSystemFont(ofSize: 40.0 / 3) //.systemFont(ofSize: 78.0 / 3/*scale*/)
//letterLabel.layer.cornerRadius = 23
//letterLabel.clipsToBounds = true
// self.backgroundColor = .red
// self.textAlignment = .center
// self.textColor = .white
// self.backgroundColor = .black
// self.text = String(letter).uppercased()
// self.font = .boldSystemFont(ofSize: 50.0 / 3)
self.addSubview(letterLabel)
self.isUserInteractionEnabled = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let point = touch.location(in: self.superview)
xOffset = point.x - self.center.x
yOffset = point.y - self.center.y
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let point = touch.location(in: self.superview)
self.center = CGPoint(x: point.x - xOffset, y: point.y - yOffset)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touchesMoved(touches, with: event)
dragDelegate?.tileView(tileView: self, didDragToPoint: self.center)
}
}
2.TargetView.swift
import UIKit
class TargetView: UILabel {
var letter: Character!
var isMatch = false
init(letter: Character, sideLength: CGFloat) {
self.letter = letter
//let image = UIImage(named: "slot")
//super.init(image: image)
//let scale = CGRect(x: 0, y: 0, width: (image?.size.width)! * scale, height: (image?.size.height)! * scale)
super.init(frame: CGRect(x: 0, y: 0, width: 15, height: 30))
self.backgroundColor = .red
self.textAlignment = .center
self.font = .boldSystemFont(ofSize: 60.0 / 3)
self.text = "_"//String(letter).uppercased()
self.textColor = .white
self.lineBreakMode = .byWordWrapping
self.adjustsFontSizeToFitWidth = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
3. ViewController
import UIKit
class ViewController: UIViewController {
// let ScreenWidth = UIScreen.main.bounds.size.width
// let ScreenHeight = UIScreen.main.bounds.size.height
//frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
//var gameView = UIView()
private var tiles = [TileView]()
private var targets = [TargetView]()
let tileMargin : CGFloat = 0.0
var words = "Hellomyf rie nds?"
// lazy var stackView: UIStackView = {
// let stack = UIStackView()
// stack.axis = .horizontal
// stack.spacing = 0
// stack.distribution = .fillEqually
// stack.translatesAutoresizingMaskIntoConstraints = false
// return stack
// }()
//
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
dealRandomAnagram()
}
func dealRandomAnagram() {
let anagram1 = words
let anagram1length = anagram1.count
print("phrase1[\(anagram1length)]: \(anagram1)")
// Рассчет размера плитки //Tile size calculation
let tileSize = ceil(UIScreen.main.bounds.size.width * 0.8 / CGFloat(min(anagram1length, anagram1length))) + tileMargin // * 1
//Поиск х позиции первой плитки // Finding x position of the first tile
var xOffset = (UIScreen.main.bounds.size.width - CGFloat(max(anagram1length, anagram1length)) * (15 + tileMargin)) / 2.0
//+ tileMargin
//let yOffset = (UIScreen.main.bounds.size.height - CGFloat(max(anagram1length, anagram1length)) * (15 + tileMargin)) / 2.0
//+ tileMargin
//настройка центра плитки (вместо источника плитки)
xOffset += tileSize / 2.0
tiles = []
// создание плитки
for (index, letter) in anagram1.enumerated() {
if letter != " " {
let tile = TileView(letter: letter, sideLength: tileSize)
tile.center = CGPoint(x: xOffset + CGFloat(index) * (tileSize + tileMargin), y: UIScreen.main.bounds.size.height/4*3)
view.addSubview(tile)
tile.dragDelegate = self
tiles.append(tile)
}
}
targets = []
for (index,letter) in anagram1.enumerated() {
let target = TargetView(letter: letter, sideLength: 15)
if letter != " " || index > 15 {
// let target = TargetView(letter: letter, sideLength: 15)
target.center = CGPoint(x: xOffset + CGFloat(index) /* * 20 */ * (15 + tileMargin), y: UIScreen.main.bounds.size.height - 100) //100 //UIScreen.main.bounds.size.height - CGFloat(index) * 50
view.addSubview(target)
targets.append(target)
//stackView.addArrangedSubview(target)
//targets.append(target)
}
}
}
//xOffset + CGFloat(index) /* * 20 */ * (tileSize + tileMargin )
//UIScreen.main.bounds.size.height - 100
func placeTile(tileView: TileView, targetView: TargetView) {
tileView.isMatch = true
targetView.isMatch = true
tileView.isUserInteractionEnabled = false
UIView.animate(withDuration: 0.35, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
tileView.center = targetView.center
tileView.backgroundColor = .clear
targetView.transform = .identity
}, completion: {
(value: Bool) in
targetView.isHidden = true
})
}
func checkValidate() {
for targetView in targets {
if !targetView.isMatch {
return
}
}
print("Valid")
}
}
extension ViewController: TileDragDelegateProtocol {
func tileView(tileView: TileView, didDragToPoint: CGPoint) {
var targetView: TargetView?
for tv in targets {
if tv.frame.contains(didDragToPoint) && !tv.isMatch {
targetView = tv
break
}
}
if let targetView = targetView {
//2 check if letter matches
if targetView.letter == tileView.letter {
//3
self.placeTile(tileView: tileView, targetView: targetView)
print("Success! You should place the tile here!")
//more stuff to do on success here
self.checkValidate()
print("Check if the player has completed the phrase")
} else {
//4
UIView.animate(withDuration:0.35,
delay:0.00,
options :UIView.AnimationOptions.curveEaseOut,
animations: {
tileView.center = CGPoint(x: tileView.center.x,
y: tileView.center.y - (200))
},
completion: nil)
print("Failure. Let the player know this tile doesn't belong here")
//more stuff to do on failure here
}
}
}
}

how to change the size of a uiimageview appended to a empty array

My swift code uses a button to place imageviews on a uiviewcontroller that parent from a empty array. The problem is I dont know how to effect the size of the invidicual imageview after it is placed on the uiview controller. When a imageview is click and then when the slider is moved the imageview should change size. However what is happening is the value when the slider is changed effects the next iamgeview that is placed on the uiviewcontroller. Pitcure below is included I circled what the slider should effect. This is a video of what I am trying to do https://www.youtube.com/watch?v=ho6ID6XVEYw&feature=youtu.be.
import UIKit
class ViewController: UIViewController {
var sx = UISlider()
var count: Int = 0
var ht = -90
var ww = 80
var arrTextFields = [UIImageView]()
var b7 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[b7,sx].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
b7.frame = CGRect(x: view.center.x-115, y: view.center.y + 200, width: 70, height: 40)
sx.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 70, height: 40)
b7.addTarget(self, action: #selector(addBOx), for: .touchUpInside)
}
//func that adds imageview.
#objc func addBOx() {
let subview = UIImageView()
subview.isUserInteractionEnabled = true
arrTextFields.append(subview)
view.addSubview(subview)
sx.addTarget(self, action: #selector(ji), for: .valueChanged)
sx.minimumValue = 10
sx.maximumValue = 150
subview.frame = CGRect(x: view.bounds.midX - 0, y: view.bounds.midY + CGFloat(ht), width: CGFloat(ww), height: 35)
subview.backgroundColor = .purple
subview.tag = count
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestured(_:)))
subview.addGestureRecognizer(pan)
count += 1
ht += 50
arrTextFields.append(subview)
}
#objc func handlePanGestured(_ gesture: UIPanGestureRecognizer) {
let draggedView = gesture.view!
view.bringSubviewToFront(draggedView)
let translation = gesture.translation(in: view)
draggedView.center = CGPoint(x: draggedView.center.x + translation.x, y: draggedView.center.y + translation.y)
gesture.setTranslation(.zero, in: view)
}
#objc func ji(sender : UISlider){
ww = Int(sx.value)
}}
The main idea that needs to be implemented is to add the currentView variable to find out which view we want to change.
We also need to add some functionality to select the specific view that needs to be changed.
1) Add variable currentView
class ViewController: UIViewController {
...
var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
2) Add tapGestureRecognozer to func addBOx()
#objc func addBOx() {
...
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGestured(_:)))
subview.addGestureRecognizer(tapGesture)
}
3) Add handleTapGestured(_:)
#objc func handleTapGestured(_ gesture: UIPanGestureRecognizer) {
currentView = gesture.view
}
4) Update func ji(sender: UISlider)
#objc func ji(sender: UISlider) {
...
currentView?.bounds.size.width = CGFloat(sx.value)
}
All code:
import UIKit
class ViewController: UIViewController {
var sx = UISlider()
var count: Int = 0
var ht = -90
var ww = 80
var arrTextFields = [UIImageView]()
var b7 = UIButton()
var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
[b7,sx].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
b7.frame = CGRect(x: view.center.x-115, y: view.center.y + 200, width: 70, height: 40)
sx.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 70, height: 40)
b7.addTarget(self, action: #selector(addBOx), for: .touchUpInside)
}
//func that adds imageview.
#objc func addBOx() {
let subview = UIImageView()
subview.isUserInteractionEnabled = true
arrTextFields.append(subview)
view.addSubview(subview)
sx.addTarget(self, action: #selector(ji), for: .valueChanged)
sx.minimumValue = 10
sx.maximumValue = 150
subview.frame = CGRect(x: view.bounds.midX - 0, y: view.bounds.midY + CGFloat(ht), width: CGFloat(ww), height: 35)
subview.backgroundColor = .purple
subview.tag = count
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestured(_:)))
subview.addGestureRecognizer(pan)
count += 1
ht += 50
arrTextFields.append(subview)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGestured(_:)))
subview.addGestureRecognizer(tapGesture)
}
#objc func handleTapGestured(_ gesture: UIPanGestureRecognizer) {
currentView = gesture.view
}
#objc func handlePanGestured(_ gesture: UIPanGestureRecognizer) {
let draggedView = gesture.view!
view.bringSubviewToFront(draggedView)
let translation = gesture.translation(in: view)
draggedView.center = CGPoint(x: draggedView.center.x + translation.x, y: draggedView.center.y + translation.y)
gesture.setTranslation(.zero, in: view)
}
#objc func ji(sender : UISlider){
ww = Int(sx.value)
currentView?.bounds.size.width = CGFloat(sx.value)
}
}
Alternatively, you can add a textField and write the number that the element from your array needs to change.
And please, don't use variables like sx, ht, ww, b7 in your code.
You can read API Design Guidelines here

How to insert data to a textfield that goes to an empty array and in turn displays in a label?

I found this ready code that prints out an already stored array called (let dataArr = ["25","51","57","73","68"]) in rows into a label.
I have now made a text field that can insert to another array(my array = String) I want this array to be swaped with the first array.
But know when I swap to empty array that has already been populated via a text field it doesn’t appear in label.
I was hoping to swap the dataArr to an empty array called array that files up with data when inserted via the text field but for reason when I am doing that it doesn’t get shown in the label I am posting now to see what I am doing wrong.
But when I print the array it appears in the console but not in the label.
let dataArr = ["2,5","5,1","5,7"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}
var array = [String]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<array.count {
let element = array[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}
Ran your code and got the following result.
Is this what you were expecting?
UPDATE :
Here's what whipped up.
lazy var myText: UITextField = {
let input = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
input.backgroundColor = .white
input.placeholder = "Enter data here!"
return input
}()
lazy var enterButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = .gray
button.setTitle("ENTER", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 11)
button.addTarget(self, action: #selector(btnEnter), for: .touchUpInside)
return button
}()
lazy var doneButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = .gray
button.setTitle("Done", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 11)
button.addTarget(self, action: #selector(btnDone), for: .touchUpInside)
return button
}()
var array = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if (!array.isEmpty) {
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<array.count {
let element = array[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
myText.removeFromSuperview()
enterButton.removeFromSuperview()
doneButton.removeFromSuperview()
self.view.addSubview(labelNum)
}
}
else
{
view.backgroundColor = .green
setUpMyText()
setUpEnterButton()
setUpDoneButton()
}
}
private func setUpMyText() {
self.view.addSubview(myText)
myText.translatesAutoresizingMaskIntoConstraints = false
myText.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
myText.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
myText.widthAnchor.constraint(equalToConstant: myText.bounds.width).isActive = true
myText.heightAnchor.constraint(equalToConstant: myText.bounds.height).isActive = true
}
private func setUpEnterButton() {
self.view.addSubview(enterButton)
enterButton.translatesAutoresizingMaskIntoConstraints = false
enterButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
enterButton.topAnchor.constraint(equalTo: myText.bottomAnchor, constant: 10).isActive = true
}
private func setUpDoneButton() {
self.view.addSubview(doneButton)
doneButton.translatesAutoresizingMaskIntoConstraints = false
doneButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
doneButton.topAnchor.constraint(equalTo: enterButton.bottomAnchor, constant: 10).isActive = true
}
#objc fileprivate func btnEnter() {
print(myText)
array.append(myText.text!)
myText.text = ""
}
#objc fileprivate func btnDone() {
print("Done")
self.viewDidLoad()
}

Swift 3 JSON to array of objects is very slow

I am upgrading my app from swift 2 to swift 3 and have a significant performance loss when building an array of custom objects. In swift 2 this took a few seconds while in swift 3 it takes around 30 seconds. I am using alamofire to return swiftyJSON, returning about 3000 rows of data. the alamofire return is quick, its looping through this json to build array of custom objects thats slow. These objects greatly simplify the code written when building table cells and passing data to new views.
Code:
class Customer {
var ID: String!
var sysName: String!
var address: String!
var contactID: String!
required init(_name:String?, _id: String?, _address:String?, _contactID:String?) {
//print(json)
if _id != nil {
self.ID = _id
}else{
self.ID = ""
}
if _name != nil {
self.sysName = _name
}else{
self.sysName = ""
}
if _address != nil {
self.address = _address
}else{
self.address = "No Address on File"
}
if _contactID != nil {
self.contactID = _contactID
}else{
self.contactID = ""
}
}
}
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
print(response.request ?? "") // original URL request
print(response.response ?? "") // URL response
print(response.data ?? "") // server data
print(response.result) // result of response serialization
if let json = response.result.value {
print("JSON: \(json)")
self.customers = JSON(json)
self.parseJSON()
}
}
func parseJSON(){
let jsonCount = self.customers["customers"].count
self.totalCustomers = jsonCount
for i in 0 ..< jsonCount {
self.loadedCustomers = i
print("customer = \(self.customers["customers"][i] ["sysName"].string!)")
//VERY SLOW
//create a customer object
let customer = Customer( _name: self.customers["customers"][i]["sysName"].string!, _id: self.customers["customers"][i]["ID"].string!, _address: self.customers["customers"][i]["mainAddr"].string!, _contactID: self.customers["customers"][i]["contactID"].string!)
//add customer to customer array
self.customersArray.append(customer)
}
self.layoutViews() //build view, call all table methods
}
Thanks
Improved code:
import Foundation
import UIKit
import Alamofire
import SwiftyJSON
enum SearchMode{
case name
case address
}
class CustomerListViewController: ViewControllerWithMenu, UITableViewDelegate, UITableViewDataSource, UISearchControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UISearchResultsUpdating{
var indicator: SDevIndicator!
var totalCustomers:Int!
//data arrays
var ids = [String]()
var names = [String]()
var addresses = [String]()
var searchController:UISearchController!
var currentSearchMode = SearchMode.name
var customerTableView:TableView = TableView()
var layoutVars:LayoutVars = LayoutVars()
var sections : [(index: Int, length :Int, title: String)] = Array()
var customersSearchResults:[String] = []
var shouldShowSearchResults:Bool = false
let viewsConstraint_V:NSArray = []
let viewsConstraint_V2:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Customer List"
view.backgroundColor = layoutVars.backgroundColor
getCustomerList()
}
func getCustomerList() {
//remove any added views (needed for table refresh
for view in self.view.subviews{
view.removeFromSuperview()
}
// Show Indicator
indicator = SDevIndicator.generate(self.view)!
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
//print(response.request ?? "") // original URL request
//print(response.response ?? "") // URL response
//print(response.data ?? "") // server data
//print(response.result) // result of response serialization
do {
if let data = response.data,
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let customers = json["customers"] as? [[String: Any]] {
for customer in customers {
if let id = customer["ID"] as? String {
self.ids.append(id)
}
if let name = customer["sysName"] as? String {
self.names.append(name)
}
if let address = customer["mainAddr"] as? String {
self.addresses.append(address)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
// build sections based on first letter(json is already sorted alphabetically)
var index = 0;
var firstCharacterArray:[String] = [" "]
for i in 0 ..< self.names.count {
let stringToTest = self.names[i].uppercased()
let firstCharacter = String(stringToTest[stringToTest.startIndex])
if(i == 0){
firstCharacterArray.append(firstCharacter)
}
if !firstCharacterArray.contains(firstCharacter) {
let title = firstCharacterArray[firstCharacterArray.count - 1]
firstCharacterArray.append(firstCharacter)
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
index = i;
}
if(i == self.names.count - 1){
let title = firstCharacterArray[firstCharacterArray.count - 1]
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
}
}
self.layoutViews()
}
}
func layoutViews(){
indicator.dismissIndicator()
searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.placeholder = "Search Customers"
searchController.searchResultsUpdater = self
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.titleView = searchController.searchBar
let items = ["Name","Address"]
let customSC = SegmentedControl(items: items)
customSC.selectedSegmentIndex = 0
customSC.addTarget(self, action: #selector(self.changeSearchOptions(sender:)), for: .valueChanged)
self.view.addSubview(customSC)
self.customerTableView.delegate = self
self.customerTableView.dataSource = self
self.customerTableView.register(CustomerTableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(self.customerTableView)
//auto layout group
let viewsDictionary = [
"view2":customSC,
"view3":self.customerTableView
]as [String:AnyObject]
let sizeVals = ["fullWidth": layoutVars.fullWidth,"width": layoutVars.fullWidth - 30,"navBottom":layoutVars.navAndStatusBarHeight,"height": self.view.frame.size.height - 100] as [String:Any]
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view2(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view3(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-navBottom-[view2(40)][view3(height)]", options: [], metrics: sizeVals, views: viewsDictionary))
}
/////////////// Search Methods ///////////////////////
func changeSearchOptions(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
currentSearchMode = .name
break
case 1:
currentSearchMode = .address
break
default:
currentSearchMode = .name
break
}
filterSearchResults()
}
func updateSearchResults(for searchController: UISearchController) {
filterSearchResults()
}
func filterSearchResults(){
customersSearchResults = []
switch currentSearchMode {
case .name:
self.customersSearchResults = self.names.filter({( aCustomer: String ) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil) })
break
case .address:
self.customersSearchResults = self.addresses.filter({( aCustomer: String) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil)
})
break
}
self.customerTableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//print("searchBarTextDidBeginEditing")
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = false
self.customerTableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !shouldShowSearchResults {
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
searchController.searchBar.resignFirstResponder()
}
/////////////// TableView Delegate Methods ///////////////////////
func numberOfSections(in tableView: UITableView) -> Int {
if shouldShowSearchResults{
return 1
}else{
return sections.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
//print("titleForHeaderInSection")
if shouldShowSearchResults{
return nil
}else{
if(sections[section].title == "#"){
return " # \(self.totalCustomers) Customers Found"
}else{
return " " + sections[section].title //hack way of indenting section text
}
}
}
func sectionIndexTitles(for tableView: UITableView) -> [String]?{
print("sectionIndexTitlesForTableView 1")
if shouldShowSearchResults{
return nil
}else{
//print("sectionIndexTitlesForTableView \(sections.map { $0.title })")
return sections.map { $0.title }
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
//print("heightForHeaderInSection")
if shouldShowSearchResults{
return 0
}else{
return 50
}
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("numberOfRowsInSection")
if shouldShowSearchResults{
return self.customersSearchResults.count
} else {
return sections[section].length
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = customerTableView.dequeueReusableCell(withIdentifier: "cell") as! CustomerTableViewCell
customerTableView.rowHeight = 50.0
if shouldShowSearchResults{
let searchString = self.searchController.searchBar.text!.lowercased()
if(currentSearchMode == .name){
cell.nameLbl.text = self.customersSearchResults[indexPath.row]
cell.name = self.customersSearchResults[indexPath.row]
if let i = self.names.index(of: cell.nameLbl.text!) {
//print("\(cell.nameLbl.text!) is at index \(i)")
cell.addressLbl.text = self.addresses[i]
cell.address = self.addresses[i]
cell.id = self.ids[i]
} else {
cell.addressLbl.text = ""
cell.address = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.name as NSString
let highlightedText = NSMutableAttributedString(string: cell.name)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.nameLbl.attributedText = highlightedText
}else{//address search mode
cell.addressLbl.text = self.customersSearchResults[indexPath.row]
cell.address = self.customersSearchResults[indexPath.row]
if let i = self.addresses.index(of: cell.addressLbl.text!) {
cell.nameLbl.text = self.names[i]
cell.name = self.names[i]
cell.id = self.ids[i]
} else {
cell.nameLbl.text = ""
cell.name = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.address as NSString
let highlightedText = NSMutableAttributedString(string: cell.address)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.addressLbl.attributedText = highlightedText
}
} else {
//print("make cell")
cell.id = self.ids[sections[indexPath.section].index + indexPath.row]
cell.name = self.names[sections[indexPath.section].index + indexPath.row]
cell.address = self.addresses[sections[indexPath.section].index + indexPath.row]
cell.nameLbl.text = self.names[sections[indexPath.section].index + indexPath.row]
cell.addressLbl.text = self.addresses[sections[indexPath.section].index + indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as! CustomerTableViewCell
let customerViewController = CustomerViewController(_customerID: currentCell.id)
navigationController?.pushViewController(customerViewController, animated: false )
tableView.deselectRow(at: indexPath!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

combine an array of images to a single image which is a row of images in swift

Trying to combine an array of images to a single image which looks like a long row of the images.
(note: images may or may not be of similar sizes)
here is the code, which currently works for single size images only
func combine(images: NSMutableArray) -> UIImage {
var contextSize = CGSizeZero
var xMaxSize : CGFloat = 0
for image in images {
//
xMaxSize = xMaxSize + image.size.width
//contextSize.width = max(contextSize.width , image.size.width )
contextSize.height = max(contextSize.height, image.size.height)
}
contextSize.width = xMaxSize
UIGraphicsBeginImageContextWithOptions(contextSize, false, UIScreen.mainScreen().scale)
var imageXPlace :CGFloat = 0
for image in images {
let originX = imageXPlace
let originY = (contextSize.height - image.size.height) / 2
image.drawInRect(CGRectMake(originX, originY, image.size.width, image.size.height))
imageXPlace = imageXPlace + image.size.width
}
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return combinedImage
}
This question is also related to this one
Images has different sizes
Height of the combined image - the smallest height in UIImage
ViewController.swift
import UIKit
struct ImageToCombine {
var image = UIImage()
var size: CGSize {
get {
return CGSize(width: image.size.width, height: image.size.height)
}
}
var widthAndHeightRatio: CGFloat {
get {
return image.size.width/image.size.height
}
}
init (image: UIImage) {
self.image = image
}
}
class ViewController: UIViewController {
var imageView = UIImageView()
var images = [ImageToCombine]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imageView = UIImageView(frame: UIScreen.mainScreen().bounds)
imageView.contentMode = .ScaleAspectFit
view.addSubview(imageView)
images.append(ImageToCombine(image: UIImage(named: "image1")!))
images.append(ImageToCombine(image: UIImage(named: "image2")!))
images.append(ImageToCombine(image: UIImage(named: "image3")!))
imageView.image = combine(images, spaceBetweenImages: 10)
}
func combine(images: [ImageToCombine], spaceBetweenImages:CGFloat) -> UIImage {
var fullWidth:CGFloat = 0
var minHeight:CGFloat = images[0].size.height
for index in 0..<images.count {
fullWidth += images[index].size.width
if (index+1 < images.count) {
fullWidth += spaceBetweenImages
}
if (minHeight > images[index].size.height) {
minHeight = images[index].size.height
}
}
let quality = CGFloat(4)
let boundForBetter = CGRectMake(0, 0, fullWidth*quality, minHeight*quality)
UIGraphicsBeginImageContext(boundForBetter.size)
let context = UIGraphicsGetCurrentContext()
for index in 0..<images.count {
var imageX:CGFloat = 0
for index2 in 0..<index {
imageX += images[index2].size.width
if (index2+1 < images.count) {
imageX += spaceBetweenImages
}
}
let imageY:CGFloat = (minHeight - images[index].size.height)*quality/2
let rect = CGRect(x: imageX*quality, y: imageY, width: images[index].size.width*quality, height: images[index].size.height*quality)
CGContextDrawImage(context, rect, images[index].image.CGImage)
}
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let resultImageimage = UIImage(CGImage: imageResize(combinedImage, sizeChange:CGSize(width: fullWidth/quality, height: minHeight/quality)).CGImage!, scale: 1.0, orientation: .DownMirrored)
return resultImageimage
}
func imageResize(imageObj:UIImage, sizeChange:CGSize)-> UIImage {
let hasAlpha = true
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
imageObj.drawInRect(CGRect(origin: CGPointZero, size: sizeChange))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() // !!!
return scaledImage
}
}
result:

Resources