When I add a shadow to my table view, it only shows if I have masksToBound = false as in picture 2. But it will look glitchy. Otherwise it looks fine scrolling up and down as in picture 1 but there's no shadow because I don't set it to false. Changing clipToBounds won't help either. My tableView changes height based on the input into the textField so I think adding a container view might be tricky.
How do I fix this? Is there a simple solution?
#IBOutlet weak var txtSearchBar: UITextField!
#IBOutlet weak var tblList: UITableView!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var shadowView: UIView!
#IBOutlet weak var tblHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var shadowHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var collectionView: UICollectionView!
var majors:[String] = Array()
var results: [String] = Array()
var label = UILabel(frame: CGRect.zero)
var originalMajorsList = ["BIO 1AL", "BIO 1B", "BIO 1A", "MCB 118","..."]
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.collectionViewLayout = columnLayout
tblList.delegate = self
tblList.dataSource = self
txtSearchBar.delegate = self
txtSearchBar.addTarget(self, action: #selector(searchRecords(_ :)), for: .editingChanged)
txtSearchBar.layer.borderColor = UIColor.groupTableViewBackground.cgColor
txtSearchBar.layer.borderWidth = 1
txtSearchBar.layer.cornerRadius = 5
//**glitchy if masksToBounds = false**
tblList.layer.borderColor = UIColor.groupTableViewBackground.cgColor
tblList.layer.borderWidth = 1
tblList.layer.cornerRadius = 5
tblList.layer.shadowColor = UIColor.lightGray.cgColor
tblList.layer.shadowOffset = CGSize(width: -10, height: 10)
tblList.layer.shadowOpacity = 0.5
tblList.layer.shadowRadius = 50
tblList.layer.masksToBounds = false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
txtSearchBar.resignFirstResponder()
return true
}
//MARK:- searchRecords
#objc func searchRecords(_ textField: UITextField) {
tblList.isHidden = false
self.majors.removeAll()
if textField.text?.count != 0 {
for major in originalMajorsList {
if let majorToSearch = textField.text{
let range = major.lowercased().range(of: majorToSearch, options: .caseInsensitive, range: nil, locale: nil)
if range != nil {
self.majors.append(major)
}
}
}
} else {
tblHeightConstraint.constant = 0
}
tblList.reloadData()
}
override func viewDidLayoutSubviews(){
tblHeightConstraint.constant = max(0, min(UIScreen.main.bounds.height * 0.4, tblList.contentSize.height))
}
//MARK:- UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return majors.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "major")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "major")
}
cell?.textLabel?.text = majors[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if !results.contains(majors[indexPath.row]) {
results.append(majors[indexPath.row])
}
textField.text = ""
tableView.isHidden = true
collectionView.reloadData()
}
Related
I have a CartItem struct where I store the items that I show in a UITableView My UITableView is in CartViewController. I use custom cell for my table view. Here is my custom cell:
class CartItemCell: UITableViewCell {
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var productPrice: UILabel!
#IBOutlet weak var productTotalPrice: UILabel!
#IBOutlet weak var stepper: UIStepper!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
var cellIndex: Int?
var product: CartItem?{
didSet{
// change the cell in table view when first
// initialized and when stepper value is changed
.
.
.
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
CartItem.cartItems[self.cellIndex!].number = Int(sender.value)
self.product?.number = CartItem.cartItems[self.cellIndex!].number
}
}
In CartViewController I have totalPrice UILabel which gets its value from CartItem.totalPrice static variable. CartItem.totalPrice's value is calculated and changed every time UIStepper in my UITableViewCell is clicked. Here is the summary of CartViewController
class CartViewController: UIViewController{
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var cartLabel: UILabel!
#IBOutlet weak var totalPrice: UILabel!
var items = CartItem.cartItems
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "CartItemCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
self.totalPrice.text = String(Int(CartItem.totalPrice))
tableView.reloadData()
}
}
extension CartViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! CartItemCell
cell.product = items[indexPath.item]
cell.cellIndex = indexPath.item
return cell
}
}
I want totalPrice label in the CartViewController changed every time CartItem.totalPrice is changed. I change the value of the CartItem.totalPrice in my CartItemCell
Thank you
Use delegate or closures to update value from tableviewcell to viewController
Protocol CartItemProtocol: AnyObject{
func valueUpdated()
}
class CartItemCell: UITableViewCell {
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var productPrice: UILabel!
#IBOutlet weak var productTotalPrice: UILabel!
#IBOutlet weak var stepper: UIStepper!
weak var delegate: CartItemProtocol?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
var cellIndex: Int?
var product: CartItem?{
didSet{
// change the cell in table view when first
// initialized and when stepper value is changed
.
.
.
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
CartItem.cartItems[self.cellIndex!].number = Int(sender.value)
self.product?.number = CartItem.cartItems[self.cellIndex!].number
self.delegate.valueUpdated()
}
}
And set delegate in cellforRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! CartItemCell
cell.product = items[indexPath.item]
cell.cellIndex = indexPath.item
cell.delegate = self
return cell
}
And Update label in delegate conformation
extension CartViewController:CartItemProtocl{
self.totalPrice.text = String(Int(CartItem.totalPrice))
}
I am trying to embed the collection view in the table view. When the page gets loaded I will retrieve the data field by field from the database and reloads the data whenever I retrieve the single field from the database. Here while reloading the table view I need to check the value i.e "oneimage" so if that value is not empty it should set to the collection view cell. The problem is whenever I scroll the table view the data in the collection view cells get swapped. Here is the code below
import UIKit
import Firebase
import FirebaseFirestore
import FirebaseAuth
import SDWebImage
struct values {
var quesvalue: String
var answvalue: String
var ImageUrl = [String]()
}
class QuestionsCell: UITableViewCell,UICollectionViewDelegate {
#IBOutlet weak var collectionview: UICollectionView!
#IBOutlet weak var card: UIView!
#IBOutlet weak var question: UILabel!
#IBOutlet weak var answer: UILabel!
#IBOutlet weak var speakbutton: UIButton!
#IBOutlet weak var collectionviewh: NSLayoutConstraint!
var imageArray = [String] ()
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
extension QuestionsCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource &
UICollectionViewDelegate>(dataSourceDelegate: D, forRow row: Int) {
collectionview.delegate = dataSourceDelegate
collectionview.dataSource = dataSourceDelegate
print("collectionviee.tag",collectionview.tag,row)
collectionview.tag = row
collectionview.contentOffset = .zero // Stops collection view if it was scrolling.
}
}
class CollectionViewCell: UICollectionViewCell{
#IBOutlet weak var backcard: UIView!
#IBOutlet weak var imageview: UIImageView!
var task: URLSessionDataTask?
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse(){
imageview.image = nil
}
}
class ViewController: UIViewController ,UITableViewDelegate,
UITableViewDataSource,UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var tableview: UITableView!
var JSONArray = [String:Any]()
var quesArray = [String]()
var ansArray = [String]()
var answer : String!
var imagesarray = [String]()
var open : [values] = []
var oneimage = [String]()
var storedOffsets = [Int: CGFloat]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
tableview.rowHeight=UITableView.automaticDimension
tableview.estimatedRowHeight=150
Firestore.firestore().collection("User").document("7ngPwZin2wg7j5JZtI0hKJO8uSA2").collection("Popop").document("7ngPwZin2wg7j5JZtI0hKJO8uSA2").collection("Answers").document("Earlyyears").getDocument() { (document, error) in
if let document = document, document.exists {
self.open.removeAll()
self.imagesarray.removeAll()
self.oneimage.removeAll()
if let b1 = document.data()!["Name"] as? [String: Any] {
print("1",b1)
if let firstName = b1["Answer"] as? String {
print("firstName is",firstName)
if firstName != "No answer recorded"{
self.answer = firstName
self.ansArray.append(firstName)
if let imageurlarray = b1["ImageURL"] as? [String] {
self.imagesarray = imageurlarray
print("imageurl array in meaning feild is",imageurlarray)
self.open.insert(values(quesvalue: self.quesArray[0],answvalue: self.answer,ImageUrl: self.imagesarray), at: 0)
self.tableview.reloadData()
}
}
}
}
if let b2 = document.data()!["Meaning"] as? [String: Any] {
print("1")
if let firstName = b2["Answer"] as? String {
print("firstName is",firstName)
if firstName != "No answer recorded"{
self.answer = firstName
self.ansArray.append(firstName)
if let imageurlarray = b2["ImageURL"] as? [String] {
self.imagesarray = imageurlarray
print("imageurl array in meaning feild is",imageurlarray)
self.open.insert(values(quesvalue: self.quesArray[1],answvalue: self.answer,ImageUrl: self.imagesarray), at: 1)
self.tableview.reloadData()
}
}
}
}
} else {
print("Document does not exist")
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ansArry.count is",open.count)
return open.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("entered into cellfor row at")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! QuestionsCell
print("quesrray,ansArray are",quesArray,ansArray,open)
if open.count > indexPath.row{
cell.question.text = open[indexPath.row].quesvalue
cell.answer.text = open[indexPath.row].answvalue
print("cell.ques.text",cell.question.text)
oneimage = open[indexPath.row].ImageUrl
print("onimage before checking",oneimage)
if !oneimage.isEmpty{
print("entered into oneimage not empty",oneimage)
cell.collectionview.isHidden = false
cell.collectionviewh.constant = 160
cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
}
else{
print("dont show collection view")
cell.collectionview.isHidden = true
cell.collectionviewh.constant = 0
}
}
else{
print("<")
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("imagesarray.count is",oneimage.count)
print("oneimage.count")
return oneimage.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: CollectionViewCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as? CollectionViewCell)!
if oneimage.count > indexPath.row{
if oneimage != [""] {
let image = oneimage[indexPath.row]
print("oneimage is",image)
print("entered into oneimage not empty")
cell.imageview.sd_setImage(with: URL(string: image))
}
}
return cell
}
Here are the screenshots of my output.
As I mentioned in the comments, this is because of the reusability. That means when a cell goes out from bottom/top, the same cell (containing the previous setup) comes in from top/bottom. So if you set something async, like a remote image on it, it may be visible on incorrect cell. You should make sure you are selecting correct cell when you are about to set the image on it.
For example you should change this:
cell.imageview.sd_setImage(with: URL(string: image))
to something like this:
(collectionView.cellForItem(at: indexPath) as? CollectionViewCell)?.imageview.sd_setImage(with: URL(string: image))
This will ask the collectionView for the real cell instead of the reused one. I don't know how sd library works, but you may want to do this in the completionHandler of the library.
Maybe this article could help you.
when I output the test result in my application, I get
fatal error: array index out of range
In the code I marked the place where the error occurred. What could be the cause of the error?
import UIKit
import RealmSwift
class ResultVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var message = ""
var result = 0
var testedVerbs = [Verb]()
var repeatTestedVerbs = [Verb]()
#IBOutlet weak var resultLabel:UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var repeatVerbs: UILabel!
#IBOutlet weak var nextButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
setUp()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testedVerbs.isEmpty ? 0 : testedVerbs.count
}
private func setUp() {
nextButton.setUpButton(button: nextButton, color: white, tintColor: black, cornerRadius: 12)
for verb in testedVerbs {
if verb.progress <= 0.49 {
repeatTestedVerbs.append(verb)
}
}
if repeatTestedVerbs.count <= 3 {//testedVerbs.count/20 * 100 {
view.backgroundColor = orange
message = "You Can Do Better!"
} else {
view.backgroundColor = green
message = "Good Job!"
}
resultLabel.text = "\(message) \(result) / \(testedVerbs.count)"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.backgroundColor = .clear
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ResultCell
// MARK: Index out of range
let verb = repeatTestedVerbs[indexPath.row]
cell.infinitiv.text = verb.infinitiv
cell.translate.text = verb.translate
cell.level.text = verb.level
cell.backgroundColor = .clear
return cell
}
static func storyboardInstance() -> ResultVC? {
let storyboard = UIStoryboard(name: String(describing: self), bundle: nil)
return storyboard.instantiateInitialViewController() as? ResultVC
}
#IBAction func repeatButton(_ sender: UIButton) {
if let nvc = navigationController {
nvc.popViewController(animated: true)
}
}
#IBAction func cancel(_ sender:UIButton) {
if let nvc = navigationController {
for vc in nvc.viewControllers {
if vc is ThemeTVC {
navigationController?.popToViewController(vc, animated: true)
break
}
}
}
}
}
Your numberOfRows is based on testedVerbs, but your cellForRowAt reads from repeatTestedVerbs.
I don't know why I can't view my pets in the table.
Few days ago the app is working perfect but when I updated the last version of Xcode the app crashed, I can't see the data whet the App run.
I new developer in swift
Help me, please.
------------------------------Model------------------------
import UIKit
struct InfoPet{
let PetPicture: String
let PetBackground: String
let PetName: String
let PetAge: String
static func InfoPets() -> [InfoPet]{
let pet1 = InfoPet(PetPicture: "Drako", PetBackground: "Background Can", PetName: "Drako", PetAge: "2 years old")
let pet2 = InfoPet(PetPicture: "Lucky", PetBackground: "Background Cat", PetName: "Lucky", PetAge: "3 years old")
return [pet1, pet2]
}
}
-----------------------TableViewController----------------
class PetsTableViewController: UITableViewController {
// MARK: - Global Var
var pets: [InfoPet] = InfoPet.InfoPets()
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pets.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PetCell", for: indexPath) as! PetsTableViewCell
let pet = pets[indexPath.row]
cell.pet = pet
return cell
}
// MARK: - UITableViewDalegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
-----------------------tableViewCell-----------------------
class PetsTableViewCell: UITableViewCell {
#IBOutlet weak var ImgPhotoPet: UIImageView!
#IBOutlet weak var ImgBackgroundPet: UIImageView!
#IBOutlet weak var LblNamePet: UILabel!
#IBOutlet weak var LblAgePet: UILabel!
var pet: InfoPet!{
didSet{
updateUI()
}
}
func updateUI() {
ImgBackgroundPet.image = UIImage(named: pet.PetBackground)
ImgPhotoPet.image = UIImage(named: pet.PetPicture)
ImgPhotoPet.layer.cornerRadius = 10.0
ImgPhotoPet.layer.masksToBounds = true
LblNamePet.text = pet.PetName
LblAgePet.text = pet.PetAge
}
}
Add Cell Identifier as "PetCell"
and add viewDidLoad method in PetsTableViewController class.
create IBOutlet of the table view.
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
}
Updated Code Below
I am working on comment cells who are limited to 100 characters and if they contain more a "show more button" will show up.
If pressed, the exact cell should reload itself with the number of lines changed to 0 and fully display the cell, no matter how big.
What I have achieved is that cells reload, but not the selected one and kinda arbitrary.
Below is my code for the enlarging process
NOTE: Updatet Code for My Function
Problem: I have to press the button twice to get the result, to minimize and to maximize the cell
#IBAction func readMore(_ sender: UIButton) {
self.state = !self.state
print("state" , state)
self.tapMore.setTitle(self.state ? self.decreaseState: self.expandState, for: .normal)
self.commentLabel.numberOfLines = (self.state ? self.expandedLines: self.numberOfLines)
print(self.commentLabel.numberOfLines)
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.parentViewControllerCommentCell?.tableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
}
The index comes from
extension CommentTableViewCell {
var indexPath: IndexPath? {
return (superview as? UITableView)?.indexPath(for: self)
}
}
Note
The print statement prints out the chosen cell ( e.g. [0, 1] or [0,0] but it doesn't change then.
Whereas I hardcode my code and change
let myIndexPath = IndexPath(row: indexPath!.row, section: 0)
to
let myIndexPath = IndexPath(row: 0, section: 0)
The feature works, but arbitrarily reloads some cells and arbitrarily enlarges and decreases the cell.
In the variable version with row: indexPath!.row the lines state doesn't change as well, whereas with hardcoded the lines change between 3 and 0.
Thanks for your help :)
Addition
my commentCell
class CommentTableViewCell: UITableViewCell {
#IBOutlet weak var likeCountButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var commentLabel: KILabel!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var tapMore: UIButton!
#IBOutlet weak var tapMoreButton: UIButton!
var delegate: CommentTableViewCellDelegate?
var postId : String!
Here is a better approach to get you the correct index path. First, in your cellForRow method, add the current index row as tag to your show more button, and then add click action to your button handler function.
Add an outlet of UIButton in you custom UITableViewCell class as
class CustomCell: UITableViewCell {
#IBOutlet var moreButton: UIButton! // Connect your button from storyboard
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.moreButton.tag = indexPath.row
/* Just add action normally from storyboard. No need to add target. cell.moreButton.addTarget(self, action:#selector(buttonUp(sender:)), for: .touchUpInside) */
return cell
}
Then in your handler function, you can get the correct index path by reading this tag
func tapForMore(sender: UIButton) {
let myIndexPath = IndexPath(row: sender.tag, section: 0)
print("myindex", myIndexPath)
//... other code here
}
You take class variable and track tap counts. Depending on these variables you can increase or decrease size of cell and reload it.
In YOURViewController declare variables as:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var CommentsTableView: UITableView!
var defaultSizeOfCell = 60.0
var newSize = 80.0
var selectedIndex = -1
var isExpanded = false
var expandCounter = 0
override func viewDidLoad() { ...
Connect button in cell to this action:
#IBAction func moreButtonAction(_ sender: UIButton) {
if !isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = true
selectedIndex = sender.tag
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Increase")
}
} else if isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = false
selectedIndex = -1
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Decrease")
}
}
}
In tableview datasource function add tag to button:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath) as! TestTableViewCell
cell.moreButton.tag = indexPath.row
return cell
}
And finally add this delegate method for height of cells:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedIndex == indexPath.row {
return CGFloat(newSize)
} else {
return CGFloat(defaultSizeOfCell)
}
}
Not to mention, button should be in cell and connected to YOURCustomTableViewCell class as:
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var moreButton: UIButton!
I have tested it against your requirements.