Delete tableview cell with a custom fuction - swift

I have create a custom delete function that i called toDoItemDeleted but there seem to be an error
.
.
Any idea how to solve it?( i m following this tutorial)
The tableview
class TodolistTable: UITableViewController , TableViewCellDelegate {
var todoItems: [DataSource] = []
override func viewDidLoad() {
self.tableView.reloadData()
tableView.backgroundView = UIImageView(image: UIImage(named: "backgroundTableView"))
tableView.dataSource = self
tableView.delegate = self
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.separatorStyle = .None
tableView.backgroundColor = UIColor.blackColor()
tableView.rowHeight = 50;
}
override func viewWillAppear(animated: Bool) {
self.tableView.reloadData()
}
#IBAction func unwinTodlist (segue: UIStoryboardSegue){
let source = segue.sourceViewController as! ViewController
let todoItemx : DataSource = source.todoitemer
if todoItemx.itenName != "" {
self.todoItems.append(todoItemx)
self.tableView.reloadData()
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return todoItems.count }
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let TempCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
as! TableViewCell
let todoItem = todoItems[indexPath.row]
//TempCell.textLabel?.backgroundColor = UIColor.clearColor()
TempCell.selectionStyle = .None
TempCell.textLabel?.text = todoItem.itenName
TempCell.delegate = self
TempCell.Item = todoItem
return TempCell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
let tappedItem = todoItems [indexPath.row] as DataSource
tappedItem.completed = !tappedItem.completed
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimation.None)
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath
indexPath: NSIndexPath) -> CGFloat {
return tableView.rowHeight;
}
func toDoItemDeleted(Item2: DataSource) {
// could use this to get index when Swift Array indexOfObject works
// let index = toDoItems.indexOfObject(toDoItem)
// in the meantime, scan the array to find index of item to delete
let index = (todoItems as NSArray).indexOfObject(Item2)
if index == NSNotFound{return}
// could removeAtIndex in the loop but keep it here for when indexOfObject works
todoItems.removeAtIndex(index)
// use the UITableView to animate the removal of this row
tableView.beginUpdates()
let indexPathForRow = NSIndexPath(forRow: index, inSection: 0)
tableView.deleteRowsAtIndexPaths([indexPathForRow], withRowAnimation: .Fade)
tableView.endUpdates()
}
// MARK: - Table view delegate
func colorForIndex(index: Int) -> UIColor {
let itemCount = todoItems.count - 1
let val = (CGFloat(index) / CGFloat(itemCount)) * 0.6
return UIColor(red: 1.0, green: val, blue: 0.0, alpha: 1.0)
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
cell.backgroundColor = colorForIndex(indexPath.row)
}
}
The tableviewCell
import UIKit
import QuartzCore
// A protocol that the TableViewCell uses to inform its delegate of state change
protocol TableViewCellDelegate {
// indicates that the given item has been deleted
func toDoItemDeleted(todoItem: DataSource)
}
class TableViewCell: UITableViewCell {
let gradientLayer = CAGradientLayer()
var originalCenter = CGPoint()
var deleteOnDragRelease = false, completeOnDragRelease = false
var tickLabel: UILabel, crossLabel: UILabel
let label: StrikeThroughText
var itemCompleteLayer = CALayer()
// The object that acts as delegate for this cell.
var delegate: TableViewCellDelegate?
// The item that this cell renders.
var Item: DataSource? {
didSet {
//label.text = Item!.text
label.strikeThrough = Item!.completed
itemCompleteLayer.hidden = !label.strikeThrough
}
}
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
// create a label that renders the to-do item text
label = StrikeThroughText(frame: CGRect.null)
label.textColor = UIColor.whiteColor()
label.font = UIFont.boldSystemFontOfSize(16)
label.backgroundColor = UIColor.clearColor()
// utility method for creating the contextual cues
func createCueLabel() -> UILabel {
let label = UILabel(frame: CGRect.null)
label.textColor = UIColor.whiteColor()
label.font = UIFont.boldSystemFontOfSize(32.0)
label.backgroundColor = UIColor.clearColor()
return label
}
// tick and cross labels for context cues
tickLabel = createCueLabel()
tickLabel.text = "\u{2713}"
tickLabel.textAlignment = .Right
crossLabel = createCueLabel()
crossLabel.text = "\u{2717}"
crossLabel.textAlignment = .Left
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(label)
addSubview(tickLabel)
addSubview(crossLabel)
// remove the default blue highlight for selected cells
selectionStyle = .None
// gradient layer for cell
gradientLayer.frame = bounds
let color1 = UIColor(white: 1.0, alpha: 0.2).CGColor as CGColorRef
let color2 = UIColor(white: 1.0, alpha: 0.1).CGColor as CGColorRef
let color3 = UIColor.clearColor().CGColor as CGColorRef
let color4 = UIColor(white: 0.0, alpha: 0.1).CGColor as CGColorRef
gradientLayer.colors = [color1, color2, color3, color4]
gradientLayer.locations = [0.0, 0.01, 0.95, 1.0]
layer.insertSublayer(gradientLayer, atIndex: 0)
// add a layer that renders a green background when an item is complete
itemCompleteLayer = CALayer(layer: layer)
itemCompleteLayer.backgroundColor = UIColor(red: 0.0, green: 0.6, blue: 0.0, alpha: 1.0).CGColor
itemCompleteLayer.hidden = true
layer.insertSublayer(itemCompleteLayer, atIndex: 0)
// add a pan recognizer
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(TableViewCell.handlePan(_:)))
recognizer.delegate = self
addGestureRecognizer(recognizer)
}
let kLabelLeftMargin: CGFloat = 15.0
let kUICuesMargin: CGFloat = 10.0, kUICuesWidth: CGFloat = 50.0
override func layoutSubviews() {
super.layoutSubviews()
// ensure the gradient layer occupies the full bounds
gradientLayer.frame = bounds
itemCompleteLayer.frame = bounds
label.frame = CGRect(x: kLabelLeftMargin, y: 0,
width: bounds.size.width - kLabelLeftMargin, height: bounds.size.height)
tickLabel.frame = CGRect(x: -kUICuesWidth - kUICuesMargin, y: 0,
width: kUICuesWidth, height: bounds.size.height)
crossLabel.frame = CGRect(x: bounds.size.width + kUICuesMargin, y: 0,
width: kUICuesWidth, height: bounds.size.height)
}
//MARK: - horizontal pan gesture methods
func handlePan(recognizer: UIPanGestureRecognizer) {
// 1
if recognizer.state == .Began {
// when the gesture begins, record the current center location
originalCenter = center
}
// 2
if recognizer.state == .Changed {
let translation = recognizer.translationInView(self)
center = CGPointMake(originalCenter.x + translation.x, originalCenter.y)
// has the user dragged the item far enough to initiate a delete/complete?
deleteOnDragRelease = frame.origin.x < -frame.size.width / 2.0
completeOnDragRelease = frame.origin.x > frame.size.width / 2.0
// fade the contextual clues
let cueAlpha = fabs(frame.origin.x) / (frame.size.width / 2.0)
tickLabel.alpha = cueAlpha
crossLabel.alpha = cueAlpha
// indicate when the user has pulled the item far enough to invoke the given action
tickLabel.textColor = completeOnDragRelease ? UIColor.greenColor() : UIColor.whiteColor()
crossLabel.textColor = deleteOnDragRelease ? UIColor.redColor() : UIColor.whiteColor()
}
// 3
if recognizer.state == .Ended {
let originalFrame = CGRect(x: 0, y: frame.origin.y,
width: bounds.size.width, height: bounds.size.height)
if deleteOnDragRelease {
if delegate != nil && Item != nil {
// notify the delegate that this item should be deleted
delegate!.toDoItemDeleted(Item!)
}
} else if completeOnDragRelease {
if Item != nil {
Item!.completed = true
}
label.strikeThrough = true
itemCompleteLayer.hidden = false
UIView.animateWithDuration(0.2, animations: {self.frame = originalFrame})
} else {
UIView.animateWithDuration(0.2, animations: {self.frame = originalFrame})
}
}
}
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
let translation = panGestureRecognizer.translationInView(superview!)
if fabs(translation.x) > fabs(translation.y) {
return true
}
return false
}
return false
}
}

Don't use a UIGestureRecognizer. You can create delete actions easily:
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// delete object
}
}

Related

TableView displaying same array filtered elements even though array elements not filtered

enter image description here I have 2 buttons and two array list items which shows fruit names and color of the fruit. I implemented search functionality to search fruit and color. The issue here is
Tap on 1st button shows fruit names in a tableview.
Filtered a fruit with name. It retrieves a fruit back
Now tap on outside of the tableview. It dismisses the tableview
Tap on second button fruit color.
Issue arrises. It showing first array elements with search keyword same I entered in the earlier search. Here is my code and screen shots for reference. Where I need to modify the code. Any help is appreciated.
import UIKit
class Cell : UITableViewCell {
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate{
#IBOutlet weak var selectFruit: UIButton! {
didSet {
selectFruit.layer.cornerRadius = 5
}
}
#IBOutlet var selectColor: UIButton! {
didSet {
selectColor.layer.cornerRadius = 5
}
}
let transparentView = UIView()
let tableView = UITableView()
var button = UIButton()
var data = [String]()
var searchData = [String]()
var searching : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(Cell.self, forCellReuseIdentifier: "Cell")
searchData = data
}
func addTransparentView(frame : CGRect) {
transparentView.frame = self.view.frame
self.view.addSubview(transparentView)
tableView.frame = CGRect(x: frame.origin.x, y: frame.origin.y + frame.height, width: frame.width, height: 0)
self.view.addSubview(tableView)
tableView.layer.cornerRadius = 5
transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
tableView.reloadData()
let tapgesture = UITapGestureRecognizer(target: self, action: #selector(deleteTransparentView))
transparentView.addGestureRecognizer(tapgesture)
transparentView.alpha = 0
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
self.transparentView.alpha = 0.5
self.tableView.frame = CGRect(x: frame.origin.x, y: frame.origin.y + frame.height + 5, width: frame.width, height: CGFloat(self.data.count * 50))
}, completion: nil)
}
#objc func deleteTransparentView() {
let frame = button.frame
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
self.transparentView.alpha = 0
self.tableView.frame = CGRect(x: frame.origin.x, y: frame.origin.y + frame.height, width: frame.width, height: 0)
}, completion: nil)
}
#IBAction func selectFruit(_ sender: Any) {
data = ["Apple","Apricots","Avocados","Oranges","Banana","Grapes","Kiwi","JackFruit","Blueberries","Boysenberries"]
button = selectFruit
addTransparentView(frame: selectFruit.frame)
}
#IBAction func selectColor(_ sender: Any) {
data = ["Red","Red1","Red2","Red3","Red4","Purple","Purple1","Purple3","Black","LightGreen","Red5"]
button = selectColor
addTransparentView(frame: selectColor.frame)
}
func searchBar(_ searchBar: UISearchBar, textDidChange textSearched: String) {
if textSearched.isEmpty {
searching = false
searchData.removeAll()
} else {
searching = true
searchData = data.filter{$0.lowercased().contains(textSearched.lowercased())
}
}
tableView.reloadData()
}
// MARK:- TableView Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchData.count
} else {
return data.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if searching {
cell.textLabel?.text = searchData[indexPath.row]
} else {
//Todo: Implement Guard or if here
cell.textLabel?.text = data[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let searchBar = UISearchBar(frame: CGRect(x: 10, y: 10, width: tableView.frame.width-20, height: 36))
searchBar.searchBarStyle = UISearchBar.Style.prominent
searchBar.placeholder = " Search..."
searchBar.isTranslucent = false
searchBar.delegate = self
return searchBar
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
button.setTitle(data[indexPath.row], for: .normal)
var buttonTitle = ""
if (searchData.count > 0) {
print("You have searched the fruit \(searchData[indexPath.row])")
buttonTitle = searchData[indexPath.row]
} else {
print("You did not search anyFruit, You just selected \(data[indexPath.row])")
buttonTitle = data[indexPath.row]
}
button.setTitle(buttonTitle, for: .normal)
deleteTransparentView()
}
}
try this one
lazy var searchBar:UISearchBar = UISearchBar()
in viewDidLoad
searchBar.delegate = self
and call this function
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
searchBar.text = ""
return true
}

delete tableviewcell from button within inside the cell

I want my swift code to delete the tableview cell within the cell using the blue button. You can see a example of what I am trying to below in the gif. I also have a indexPath selection var. You should probably have to delete it in the deleteCell func also I assume you would use a tag. All the code is in below no need for storyboard.
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
var arr = [1,1,3,3]
var tableview = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
return cell
}
#objc func deleteCell(){
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height * 0.8)
view.addSubview(tableview)
tableview.register(customtv.self, forCellReuseIdentifier: "cell")
tableview.delegate = self
tableview.dataSource = self
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var btn : UIButton = {
let btn = UIButton(frame: CGRect(x: 50, y: 6, width: 100 , height: 110))
btn.backgroundColor = .blue
return btn
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(btn)
}
}
Here's my solution:
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, CustomTvCellDelegate, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
var cellCount = 2
var tableview = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellCount // use a variable here so you can change it as you delete cells, else it will crash
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTv
cell.delegate = self
return cell
}
func deleteCell(_ customTV: CustomTv, location: CGPoint) {
// put the button location in an index path array
var deleteArray = [IndexPath]()
if let indexPath = tableview.indexPathForRow(at: location) {
deleteArray.append(indexPath)
}
cellCount -= 1 // update the row count as you delete a cell
// delete the row using the delete array with a fade animation
tableview.deleteRows(at: deleteArray, with: .fade)
}
override func viewDidLoad() {
super.viewDidLoad()
tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height * 0.8)
view.addSubview(tableview)
tableview.register(CustomTv.self, forCellReuseIdentifier: "cell")
tableview.delegate = self
tableview.dataSource = self
}
}
// you need to declare a cell delegate that will let your tableview know when the button was tapped on the cell
protocol CustomTvCellDelegate: AnyObject {
func deleteCell(_ customTV: CustomTv, location: CGPoint)
}
class CustomTv: UITableViewCell { // it's good form to Pascal case your class name ;)
weak var delegate: CustomTvCellDelegate? // make that delegate a property of your cell
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var btn : UIButton = {
let btn = UIButton(frame: CGRect(x: 50, y: 6, width: 100 , height: 110))
btn.backgroundColor = .blue
return btn
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
// moved these calls here instead of on setSelected(_selected:animated:)
addSubview(backView)
backView.addSubview(btn)
btn.addTarget(self, action:#selector(self.deleteCell), for: .touchUpInside)
}
#objc func deleteCell(sender: UIButton){
// let your cell delegate (tableview) know what the button got tapped, you need to pass the button here)
let buttonLocation = sender.superview?.convert(sender.frame.origin, to: nil) ?? CGPoint.zero
delegate?.deleteCell(self, location: buttonLocation)
}
}

How can I add drop down selections to my UITableView when user presses "add" button?

Currently, myTableView contains "One", "Two" and "Three". I would however, like to to be blank until the user selects both a make and model from the drop down options above, and once the press the "add" button I would like the data to be passed into myTableView.
import UIKit
var myTableView: UITableView = UITableView()
struct Section {
var make: String
var model: [String]
}
var Cars = [
Section(make: "Any", model: ["Any"]),
Section(make: "BMW", model: ["A","B","C"]),
Section(make: "Ford", model: ["D","E","F"]),
Section(make: "Audi", model: ["G","H","I"]),
Section(make: "Bentley", model: ["J","K","L"])
]
var carMake = Cars.map({$0.make})
class ViewController: UIViewController,DropDownBtnProtocol, UITableViewDataSource, UITableViewDelegate {
func optionChanged(_ button: DropDownBtn, string: String) {
if button == makeButton {
if let selectedMake = Cars.first(where: { $0.make == string }) {
modelButton.dropView.dropDownOptions = selectedMake.model
self.view.bringSubviewToFront(modelButton.dropView)
}
} else if button == modelButton {
}
}
////Set up buttons
var makeButton = DropDownBtn()
var modelButton = DropDownBtn()
var addButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Innocent Sheep"
view.backgroundColor = .white
makeButton = DropDownBtn.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
makeButton.delegate = self
makeButton.setTitle("Select Make", for: .normal)
makeButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
makeButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(makeButton)
makeButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
makeButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -300).isActive = true
makeButton.widthAnchor.constraint(equalToConstant: 450).isActive = true
makeButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
makeButton.dropView.dropDownOptions = carMake
modelButton = DropDownBtn.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
modelButton.delegate = self
modelButton.setTitle("Select Model", for: .normal)
modelButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
modelButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(modelButton)
modelButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
modelButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -240).isActive = true
modelButton.widthAnchor.constraint(equalToConstant: 450).isActive = true
modelButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
let addButton = UIButton.init(type: .system)
addButton.frame = CGRect(x: 50, y: 300, width: 60, height: 40)
addButton.layer.cornerRadius = 5
addButton.setTitle("Add", for: .normal)
addButton.layer.borderWidth = 2.5
addButton.layer.borderColor = UIColor.black.cgColor
addButton.addTarget(self, action: #selector(buttonClicked(_ :)), for: .touchUpInside)
self.view.addSubview(addButton)
}
#objc func buttonClicked(_: UIButton) {
print("tapped")
}
var itemsToLoad: [String] = ["One", "Two", "Three"]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = (screenSize.height / 2)
myTableView.frame = CGRect(x: 0, y: 500, width: screenWidth, height: screenHeight)
myTableView.dataSource = self
myTableView.delegate = self
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
self.view.addSubview(myTableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsToLoad.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
cell.textLabel?.text = self.itemsToLoad[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("User selected table row \(indexPath.row) and item \(itemsToLoad[indexPath.row])")
}
}
protocol DropDownBtnProtocol {
func optionChanged(_ button: DropDownBtn, string: String)
}
class DropDownBtn: UIButton, DropDownViewProtocol {
func dropDownPressed(string: String) {
self.setTitle(string, for: .normal)
self.dismissMakeDropDown()
delegate.optionChanged(self, string: string)
}
var delegate: DropDownBtnProtocol!
var dropView = DropDownView()
var height = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame:frame)
self.backgroundColor = UIColor(red: 52/255, green: 49/255, blue: 78/255, alpha: 1)
dropView = DropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubviewToFront(dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
var makeisOpen = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if makeisOpen == false {
makeisOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 150
self.superview?.bringSubviewToFront(dropView)
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0,
usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options:
.curveEaseInOut, animations: {self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
} else {
makeisOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0,
usingSpringWithDamping: 0.5,initialSpringVelocity: 0.5, options:
.curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
}
func dismissMakeDropDown() {
makeisOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0,
usingSpringWithDamping: 0.5,initialSpringVelocity: 0.5, options:
.curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
protocol DropDownViewProtocol {
func dropDownPressed(string: String)
}
class DropDownView: UIView, UITableViewDelegate,UITableViewDataSource {
var dropDownOptions = [String]() {
didSet {
tableView.reloadData()
}
}
var tableView = UITableView()
var delegate : DropDownViewProtocol!
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.white
self.backgroundColor = UIColor.white
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView)
tableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel!.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.init(red: 255/255, green: 160/255, blue: 122/255, alpha: 0.8)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
}
class DropDownView: UIView, UITableViewDelegate,UITableViewDataSource {
var optionstableView = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
optionstableView.backgroundColor = UIColor.red
self.backgroundColor = UIColor.red
optionstableView.delegate = self
optionstableView.dataSource = self
optionstableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(optionstableView)
optionstableView.leftAnchor.constraint(equalTo: optionstableView.leftAnchor, constant: 32).isActive = true
optionstableView.topAnchor.constraint(equalTo: optionstableView.topAnchor, constant: 120).isActive = true
optionstableView.rightAnchor.constraint(equalTo: optionstableView.rightAnchor, constant: -32).isActive = true
optionstableView.bottomAnchor.constraint(equalTo: optionstableView.bottomAnchor, constant: -32).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 25
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel!.text = "dropDownOptions[indexPath.row]"
cell.backgroundColor = UIColor.init(red: 255/255, green: 160/255, blue: 122/255, alpha: 0.8)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
}
Rather than myTableView showing "One","Two" and "Three" I would like it to initially contain no values but when a user selects both a make and model from the above drop down menus and they press the "add" button, the selections are added to the table view. Thank you.
First of all, I don't know if you just copy and paste stuff or your actual file looks like that. If your file actually looks like that then you need to learn the best practice of code organization and clean up a little.
Second, Not sure if you want to add a cell, a section, or what. After a quick scan of your code, I saw you programmatically created some constraints which were fine but I'd suggest with a custom view or custom UITableViewCell to contain a custom view and put your other view related constraints in there.
So let's get back on the topic, if you simply just want to let the user select two things in the drop down selections then insert the UITableViewCell then you can do it. Just need to add some logic to handle it and remember to update your display data when you insert and remove the table view cell. But I'd suggest clean up your code a bit before you add more logic to it.

Anchoring UILabel in UITableView section header

I'm getting a misalignment of a UILabel (variable name: wrongCountLabel) in a UITableView section header, as it's programatically set to a fixed x and y coordinate. These coordinates work fine for smaller screens but fall short of the right hand side on larger screens (see screen dumps below).
Since I created the section header in code I've tried to programatically anchor the trailing edge of the "Wrong (times)" label to the trailing edge of UITableView. When I run the widget, it says it's unable to load the data.
//
// TodayViewController.swift
// Widget
//
// Created by on 10/02/2019.
// Copyright © 2019. All rights reserved.
//
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var words = [String]()
var sortedPracticeWords = [String]()
var chosenLanguage = String()
let wordsString = "Words"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
if let savedLanguage = defaults.object(forKey: "languageChosen") as? String {
print("savedLanguage is: \(savedLanguage)")
chosenLanguage = savedLanguage
if let savedWords = defaults.object(forKey: "\(chosenLanguage)\(wordsString)") as? [String] {
words = savedWords
}
}
}
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
sortPracticeWords()
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
if activeDisplayMode == .compact {
preferredContentSize = CGSize(width: 0, height: 110)
} else {
preferredContentSize = CGSize(width: 0, height: 440)
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.newData)
}
func sortPracticeWords() {
var practiceWords = [String]()
for i in 1..<words.count {
if Int(words[i - 1].components(separatedBy: "::")[2]) ?? 0 > 0 {
practiceWords.append(words[i - 1])
}
}
var sortedAboveIndex = practiceWords.count
var swaps = 0
var tempPracticeWord = String()
repeat {
var lastSwapIndex = 0
for i in 1..<sortedAboveIndex {
if Int(practiceWords[i - 1].components(separatedBy: "::")[2])! < Int(practiceWords[i].components(separatedBy: "::")[2])! {
tempPracticeWord = practiceWords[i]
practiceWords[i] = practiceWords[i - 1]
practiceWords[i - 1] = tempPracticeWord
lastSwapIndex = i
swaps += 1
}
}
sortedAboveIndex = lastSwapIndex
} while (sortedAboveIndex != 0)
sortedPracticeWords = practiceWords
print("sortedPracticeWords are: \(sortedPracticeWords)")
print("practiceWords is sorted in \(swaps) swaps.")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sortedPracticeWords.count
}
internal func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(white: 1, alpha: 0.2)
let languageLabel = UILabel()
languageLabel.text = "\(chosenLanguage.capitalized)"
languageLabel.frame = CGRect(x: 10, y: 5, width: 170, height: 20)
//languageLabel.leadingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.leadingAnchor).isActive = true
view.addSubview(languageLabel)
let wrongCountLabel = UILabel()
wrongCountLabel.text = "Wrong (times)"
wrongCountLabel.textAlignment = .left
wrongCountLabel.frame = CGRect(x: 180, y: 5, width: 120, height: 20)
//wrongCountLabel.trailingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.trailingAnchor).isActive = true
view.addSubview(wrongCountLabel)
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TodayCustomCell = self.tableView.dequeueReusableCell(withIdentifier: "PracticeWord") as! TodayCustomCell
let sortedPracticeWord = sortedPracticeWords[indexPath.row]
print("practiceWord is: \(sortedPracticeWord)")
let split = sortedPracticeWord.components(separatedBy: "::")
cell.practiceWord.textColor = UIColor(white: 1, alpha: 0.75)
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView!.backgroundColor = UIColor(white: 1, alpha: 0.20)
cell.practiceWord.frame = CGRect(origin: CGPoint(x: 10, y: 10), size: CGSize(width: 200, height: 40))
cell.wrongCount.frame = CGRect(origin: CGPoint(x: 210, y: 10), size: CGSize(width: 100, height: 40))
cell.practiceWord?.text = split[1]
cell.wrongCount?.text = split[2]
print("cell is: \(cell)")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
/*if let cell = tableView.cellForRow(at: indexPath) {
let practiceWord = sortedPracticeWords[indexPath.row]
let split = practiceWord.components(separatedBy: "::")
cell.detailTextLabel?.text = split[2]
print("Detail cell is: \(cell)")
}*/
}
}
The right hand screen should display like the left hand screen, only on a bigger screen.
I need some code that anchors UILabels so they work regardless of user's screen size.
Would changing:
wrongCountLabel.textAlignment = .left
To
wrongCountLabel.textAlignment = .right
Help?

iOS11 how to sync large navigation bar collapse with scroll

Using Swift 3 with Xcode 9 I'm using the large UINavigationBar view by:
if #available(iOS 11.0, *) {
navigationBar?.prefersLargeTitles = true
UINavigationBar.appearance().largeTitleTextAttributes = [
NSForegroundColorAttributeName: Colors.black
]
}
When I scroll a UITableView, the bar collapses too fast which creates an unwanted space:
Before:
After:
The moment I touch the UITableView the bar collapses.
The tableView has the following properties:
let rect = CGRect(
x: 0,
y: UIApplication.shared.statusBarView?.frame.height ?? 20,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height
)
let tableView = UITableView(frame: rect)
The top inset of the tableView is self.navigationController?.navigationBar.frame.height ?? 44
Also the tableView is set to:
if #available(iOS 11.0, *) {
self.contentInsetAdjustmentBehavior = .never
}
The bar is translucent and I wish to keep that. What am I missing? Help is very appreciated.
I don't know whether it will useful to you or not. But its the sample code which is working for me.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
var numbers: [String] = []
let rect = CGRect(
x: 0,
y: UIApplication.shared.statusBarFrame.height ?? 20,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height
)
lazy var tableView: UITableView = {
let tV = UITableView()
tV.delegate = self
tV.dataSource = self
tV.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell")
return tV
}()
override func viewDidLoad() {
super.viewDidLoad()
numbers = generateNumbers()
self.view.backgroundColor = UIColor.white
title = "Numbers"
self.navigationController?.navigationBar.prefersLargeTitles = true
let search = UISearchController(searchResultsController: nil)
search.hidesNavigationBarDuringPresentation = false
search.searchResultsUpdater = self
search.definesPresentationContext = true
self.navigationItem.searchController = search
tableView.frame = rect
self.view.addSubview(tableView)
}
func generateNumbers() -> [String] {
var numbers = [String]()
for var i in 1..<100 {
numbers.append(String(i))
}
return numbers
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numbers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.numbers[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func updateSearchResults(for searchController: UISearchController) {
if let text = searchController.searchBar.text, !text.isEmpty {
numbers = self.numbers.filter({ (number) -> Bool in
return number.contains(text)
})
} else {
numbers = generateNumbers()
}
self.table.reloadData()
}
}
I got it working using:
if #available(iOS 11.0, *) {
self.contentInsetAdjustmentBehavior = .always
}
And
let tableViewFrame = CGRect(
x: 0,
y: UIApplication.shared.statusBarFrame.height ?? 20,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height
)
And no further TableView top insets.