Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to make expandable UITableView and make a simple rotate animation to UIImage in UITableViewCell from didSelectRowAt when I click the cell the image rotate 180° clockwise and when I click the cell again it will rotate -180° back to its normal state but the animation not working and it has a bug for the first time to click on cell the arrow not rotate I must click in twice to make it rotate as the gif.
TestModel:
struct Titles {
var opened = Bool()
var title = String()
var sectionData = [String]()
}
ViewControllerView:
class ViewControllerView: UIView {
var data = [Titles]()
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
data = [
Titles(opened: false, title: "Title1", sectionData: ["Cell1", "Cell2", "Cell3"]),
Titles(opened: false, title: "Title2", sectionData: ["Cell1", "Cell2", "Cell3"]),
Titles(opened: false, title: "Title3", sectionData: ["Cell1", "Cell2", "Cell3"])
]
}
lazy var recipesTableView: UITableView = {
let recipesTableView = UITableView()
recipesTableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
recipesTableView.register(TableViewCellTwo.self, forCellReuseIdentifier: "TableViewCellTwo")
return recipesTableView
}()
}
extension ViewControllerView: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if data[section].opened == true {
return data[section].sectionData.count + 1
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.title.text = data[indexPath.section].title
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCellTwo", for: indexPath) as! TableViewCellTwo
cell.title.text = data[indexPath.section].sectionData[indexPath.row - 1]
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if data[indexPath.section].opened == true {
data[indexPath.section].opened = false
let section = IndexSet.init(integer: indexPath.section)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
UIView.animate(withDuration: 1, animations: {
cell.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi)
})
tableView.reloadSections(section, with: .none)
} else {
data[indexPath.section].opened = true
let section = IndexSet.init(integer: indexPath.section)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
UIView.animate(withDuration: 1, animations: {
cell.arrowImage.transform = CGAffineTransform.identity
})
tableView.reloadSections(section, with: .none)
}
}
}
TableViewCell:
class TableViewCell: UITableViewCell {
lazy var arrowImage: UIImageView = {
var arrow = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill"))
arrow.tintColor = .black
arrow.translatesAutoresizingMaskIntoConstraints = false
return arrow
}()
}
First we declare an enum with available chevron directions.
enum ChevronDirection: Double {
case up = -180
case down = 0
}
Also we could add an extension to make working with radians much easier
extension Double {
var degreesToRadians: CGFloat {
return CGFloat(self) * (CGFloat(Double.pi) / 180.0)
}
}
Then you should create a UITableViewHeaderFooterView header
class AnyNameHeader: UITableViewHeaderFooterView {
#IBOutlet weak var imgChevron: UIImageView!
var tapDelegate: DelegateToGetNotified?
var currentSection: Int!
func configure(_ text: String, section: Int, isExpanded: Bool) {
currentSection = section
self.set(isExpanded: isExpanded, animated: false)
}
func set(isExpanded: Bool, animated: Bool) {
if isExpanded {
setChevronDirection(ChevronDirection.up, animated: animated)
} else {
setChevronDirection(ChevronDirection.down, animated: animated)
}
}
func setChevronDirection(_ direction: ChevronDirection, animated: Bool) {
if animated {
UIView.animate(withDuration: 0.4, delay: 0.0, options: UIViewAnimationOptions(), animations: { [weak self] in
self?.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
}, completion: nil)
} else {
self.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
}
}
//Note: You can add your way to detect user tap over header for me i add a button over the header
#IBAction func headerAction() {
tapDelegate?.didTapHeader(sectionIndex: currentSection)
}
} // End of header
Then we goes to the Final part in our View controller
class MyViewController: UIViewController {
var expandedSectionIndex : Int? = nil {
didSet{
tableView.beginUpdates()
// Collapse previously expanded section
if let oldValue = oldValue {
tableView.deleteRows(at: indexPathsFor(section: oldValue), with: UITableViewRowAnimation.top)
if let header = tableView.headerView(forSection: oldValue) as? AnyNameHeader {
header.set(isExpanded: false, animated: true)
}
}
// Don't continue to Expand new section if already collapsing opened section
if let oldValue = oldValue, let sectionIndex = expandedSectionIndex, sectionIndex == oldValue {
expandedSectionIndex = nil
tableView.endUpdates()
return
}
// Expand new section
if let expandedSectionIndex = expandedSectionIndex {
tableView.insertRows(at: indexPathsFor(section: expandedSectionIndex), with: UITableViewRowAnimation.top)
if let header = tableView.headerView(forSection: expandedSectionIndex) as? AnyNameHeader {
header.set(isExpanded: true, animated: true)
}
}
tableView.endUpdates()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isExpandedSection(section: section) {
return data[section].sectionData.count
} else {
return 0
}
}
}
// MARK: Expanded Section
extension MyViewController {
/// Returns Indexpaths to be Inserted or Removed for a given section Index
fileprivate func indexPathsFor(section: Int) -> [IndexPath] {
var newIndexPaths = [IndexPath]()
for index in 0 ..< data[section].sectionData.count {
let newIndexPath = IndexPath(row: index , section: section)
newIndexPaths.append(newIndexPath)
}
return newIndexPaths
}
fileprivate func isExpandedSection(section: Int) -> Bool {
return expandedSectionIndex == section
}
}
extension MyViewController: DelegateToGetNotified {
func didTapHeader(sectionIndex: Int) {
expandedSectionIndex = sectionIndex
}
}
I have a dynamic collectionView in TableView and there are three errors:
the cells are not displayed correctly when the application starts
when I do inserItem the cells flicker as if they are rebooting
and most importantly when collectionView.insertItems the first four cells are hidden, the video
thanks
MainViewController
class ViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setting()
}
func setting(){
getData()
tableView.dataSource = self
tableView.delegate = self
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.rowHeight = UITableView.automaticDimension
}
//load data
func pagination(_ completion: (()->())?){
SmartNetworkSevrice.getGoods(with: url) { [unowned self] (data) in
guard data.modals.count > 0 else {
self.tableView.tableFooterView = nil
return
}
self.goods.append(contentsOf: data.modals)
self.offSet += data.modals.count
DispatchQueue.main.async {
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.tableFooterView = nil
if self.goods.count == data.modals.count || self.isRefresh {
self.tableView.reloadRows(at: [indexPath], with: .none)
} else {
if let cell = self.tableView.cellForRow(at: indexPath) as? TVCellGoods {
UIView.performWithoutAnimation {
self.tableView.beginUpdates()
cell.insertGoods(data.modals)
cell.layoutIfNeeded()
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
self.tableView.endUpdates()
}
}
}
completion?()
}
}
// define bottom of tableView
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView == self.tableView else { return }
if (!isMoreDataLoading) {
// Вычислить позицию длины экрана до нижней части результатов
let scrollViewContentHeight = scrollView.contentSize.height
let scrollOffsetThreshold = scrollViewContentHeight - scrollView.bounds.size.height
if(scrollView.contentOffset.y > scrollOffsetThreshold && scrollView.isDragging) {
isMoreDataLoading = true
self.tableView.isScrollEnabled = false;
self.tableView.isScrollEnabled = true;
pagination(nil)
}
}
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "goods", for: indexPath) as? TVCellGoods else { return UITableViewCell() }
guard bestGoods.count != 0, goods.count != 0 else { return UITableViewCell() }
cell.delegate = self
cell.configure(bestGoods, goods, categories)
// автообновление высоты
self.tableView.beginUpdates()
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
self.tableView.endUpdates()
return cell
}
}
extension ViewController: UITableViewDelegate, CallDelegate {
func callMethod() {}
func callMethod(push vc:UIViewController) {
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
TableViewCell with collectionView
class TVCellGoods: UITableViewCell {
#IBOutlet weak var collectionView:UICollectionView!
#IBOutlet weak var collectionViewHeight:NSLayoutConstraint!
weak var delegate:CallDelegate?
var bestGoods = [Goods]() // лучшие товары
var goods = [Goods]() // все товары
var categories = [Menu]()
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.tag = 2
collectionView.isScrollEnabled = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configure(_ best:[Goods],_ goods:[Goods], _ category:[Menu]) {
self.bestGoods = best
self.goods = goods
self.categories = category
self.collectionView.reloadData()
}
func insertGoods(_ data:[Goods]) {
self.goods.append(contentsOf: data)
let count = self.bestGoods.count + self.categories.count + self.goods.count
let indexPaths = ((count - data.count) ..< count)
.map { IndexPath(row: $0, section: 0) }
self.collectionView.performBatchUpdates({
self.collectionView.insertItems(at: indexPaths)
}, completion: nil)
}
}
and CollectionViewCell
class CVCellGoods: UICollectionViewCell {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var price: UILabel!
#IBOutlet weak var delivery: UIImageView!
#IBOutlet weak var premium: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
delivery.image = nil
premium.image = nil
title.text = nil
price.text = nil
imageView.image = nil
imageView.sd_cancelCurrentImageLoad()
}
override func awakeFromNib() {
super.awakeFromNib()
imageView.backgroundColor = UIColor(red: 215/255, green: 215/255, blue: 215/255, alpha: 1)
self.contentView.layer.cornerRadius = 5
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0.5)
self.layer.shadowRadius = 1
self.layer.shadowOpacity = 0.3
self.layer.masksToBounds = false
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
EDIT: this gives a small bug at startup
TVCellGoods.swift
override func layoutSubviews() {
super.layoutSubviews()
self.collectionView.collectionViewLayout.invalidateLayout()
}
I just figured out how to solve your problem.
You gonna have to invalidate your collection view layout, as you are using an collectionView inside a tableView just add the following method on TVCellGoods.swift
override func layoutSubviews() {
super.layoutSubviews()
self.collectionView.collectionViewLayout.invalidateLayout()
}
I tested it, and worked!!
see video
It's not a good idea to add a scroll view inside another scroll view!
Do you have an example project in order to help you?
the cells are not displayed correctly when the application starts
You calculate collectionView itemSize in ViewDidLoad,
but in ViewDidLoad TableView & TableviewCell width not equal ViewController.
override func viewDidLoad() {
super.viewDidLoad()
print(view.frame.size.width) //320
print(tableView.frame.size.width) //414
}
You can try reloadData in ViewDidAppear.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(view.frame.size.width) //320
print(tableView.frame.size.width) //320
tableView.reloadData()
}
when collectionView.insertItems the first four cells are hidden
Subclass UICollectionViewFlowLayout
class MyCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
TVCellGoods.swift
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.tag = 2
collectionView.isScrollEnabled = false
collectionView.collectionViewLayout = MyCollectionViewFlowLayout()
}
I put UICollectionView inside table cell in Home page. Each category row has their contents image. But, my problem is that when I run the app, the images under second row/second category do not show whenever I run the app.
The images will show after I click more button and it will goes to details UI. Then, I click on Back button and reach the Home page. At this time, the images shows properly. I tried many codes but I don't know where it is wrong.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.reloadData()
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//getSearchWords()
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
override func viewDidLoad() {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// debugPrint("HOme Table View Cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "homecell") as! HomeCategoryRowCell
let list = sections[(indexPath as NSIndexPath).row]
cell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
cell.collectionViewOffset = self.storedOffsets[indexPath.row] ?? 0
DispatchQueue.main.async {
cell.categoryTitle.text = list.package_name
cell.mainAssociatedURL.text = list.package_url
}
cell.categoryTitle.font = UIFont.boldSystemFont(ofSize: 17.0)
cell.collectionView.tag = indexPath.row
cell.parentVC = self
cell.collectionView.reloadData()
return cell
}
extension Home : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sections[collectionView.tag].packageTable.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! HomeVideoCell
let list = sections[collectionView.tag].packageTable[indexPath.row]
if(list.poster_image_url != StringResource().posterURL){
let url = NSURL(string: list.poster_image_url)
do{
if let url = url {
_ = try Data(contentsOf:url as URL)
poster_url = list.poster_image_url
}
}catch let error {
//debugPrint("ERRor ::\(error)")
poster_url = StringResource().posterURL
}
}else{
poster_url = StringResource().posterURL
}
AsyncImageLoader.sharedLoader.imageForUrl(urlString: poster_url) { (image, url) -> () in
DispatchQueue.main.async(){
cell.movieTitle.text = list.name
if(url == StringResource().posterURL){
cell.imageView.image = UIImage(named: "sample_cover")
}else{
cell.imageView.image = image
}
}
}
// cell.layer.shouldRasterize = true
// cell.layer.rasterizationScale = UIScreen.main.scale
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Home Collection view at row \(collectionView.tag) selected index path \(indexPath)")
let list = sections[collectionView.tag].packageTable[indexPath.row]
self.prefs.set(list.poster_image_url, forKey: "poster_image_url")
self.prefs.set(list.name, forKey: "name")
self.prefs.set(list.assets_id, forKey: "VEDIO_ID")
debugPrint(list.name)
debugPrint(list.assets_id)
}
}
These above codes are written in Home.swift.
In HomeCell.swift,
class HomeCell : UITableViewCell {
var parentVC: UIViewController?
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var categoryTitle: UILabel!
#IBOutlet weak var SeeAll: UIButton!
#IBOutlet weak var mainAssociatedURL: UILabel!
let prefs:UserDefaults = UserDefaults.standard
var catId: String! = ""
var associated_url: String!
override func awakeFromNib() {
super.awakeFromNib()
SeeAll.setTitle(NSLocalizedString("See All >", comment: ""), for: .normal)
SeeAll.titleLabel!.font = UIFont (name: "Tharlon", size: 14)
}
#IBAction func btnSeeAll(_ sender: Any) {
catId = categoryTitle.text!
associated_url = mainAssociatedURL.text!
self.prefs.setValue(1, forKey: "PROVIDER_ID")
self.prefs.set(catId, forKey: "PROVIDER_NAME")
self.prefs.set(associated_url, forKey: "associated_url")
}
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
}
extension HomeCategoryRowCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
self.collectionView.delegate = dataSourceDelegate
self.collectionView.dataSource = dataSourceDelegate
self.collectionView.tag = row
self.collectionView.setContentOffset(self.collectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.
self.collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
get { return collectionView.contentOffset.x }
set { collectionView.contentOffset.x = newValue }
}
}
Can anyone help me please?
Edit controller like below, you are calling too many reloadData.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.tableView.layoutIfNeeded()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//getSearchWords()
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.tableView.layoutIfNeeded()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
and add:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView .deselectRow(at: indexPath, animated: false)
}
I have a very primitive problem at hand and that is when I have a tap gesture recognizer added to my custom cell I cant call the didSelectRowAt method.
My Code is the following:
import UIKit
class LanguageViewController: UIViewController {
#IBOutlet weak var titleLabel: AvenirUILabel!
#IBOutlet weak var backButton: UIButton!
#IBOutlet weak var submitButton: PinkUIButton!
#IBOutlet weak var addLanguageButton: UIButton!
#IBOutlet weak var addAnotherLanguageLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
var numberOfLanguagesSpoken = 2
var pickersStartHeight: CGFloat!
let downArrowImage = UIImage(named: "arrow_drop_down")
let BGColor = UIColor.white.withAlphaComponent(0.1)
var tag = 1001
var selectedCellHeight: CGFloat = 88.0
var unselectedCellHeight: CGFloat = 44.0
var selectedCellIndexPath: IndexPath?
let languages = ["Akan", "Amharic", "Arabic", "Assamese", "Awadhi", "Azerbaijani", "Balochi", "Belarusian", "Bengali", "Bhojpuri", "Burmese", "Cebuano", "Chewa", "Chhattisgarhi", "Chittagonian", "Czech", "Deccan", "Dhundhari", "Dutch", "Eastern Min", "English", "French", "Fula", "Gan Chinese", "German", "Greek", "Gujarati", "Haitian Creole", "Hakka", "Haryanvi", "Hausa", "Hiligaynon/Ilonggo", "Hindi ", "Hmong", "Hungarian", "Igbo", "Ilocano", "Italian", "Japanese", "Javanese", "Jin", "Kannada", "Kazakh", "Khmer", "Kinyarwanda", "Kirundi", "Konkani", "Korean", "Kurdish", "Madurese", "Magahi", "Maithili", "Malagasy", "Malay", "Malayalam", "Mandarin", "Marathi", "Marwari", "Mossi", "Nepali", "Northern Min", "Odia", "Oromo", "Pashto", "Persian", "Polish", "Portuguese", "Punjabi", "Quechua", "Romanian", "Russian", "Saraiki", "Serbo-Croatian", "Shona", "Sindhi", "Sinhalese", "Somali", "Southern Min", "Spanish", "Sundanese", "Swedish", "Sylheti", "Tagalog/Filipino", "Tamil", "Telugu", "Thai", "Turkish", "Turkmen", "Ukrainian", "Urdu", "Uyghur", "Uzbek", "Vietnamese", "Wu", "Xhosa", "Xiang ", "Yoruba", "Yue ", "Zhuang", "Zulu"]
var proficiency = ["Basic","Conversational","Proficient","Fluent","Native"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = backgroundTint
// initial set up
let height = self.view.bounds.height
titleLabel.textColor = .white
backButton.addTarget(self, action: #selector(goBack), for: .allTouchEvents)
tableView.dataSource = self
tableView.delegate = self
tableView.register(LanguageProficiencyTableViewCell.self, forCellReuseIdentifier: "Language")
tableView.backgroundColor = backgroundTint
tableView.allowsSelection = true
tableViewHeightConstraint.constant = 2*height/9
addLanguageButton.tintColor = .white
addLanguageButton.addTarget(self, action: #selector(addNewLanguage), for: .allTouchEvents)
addAnotherLanguageLabel.textColor = .white
submitButton.addTarget(self, action: #selector(submitChanges), for: .allEvents)
}
override func viewWillLayoutSubviews() {
unselectedCellHeight = self.view.bounds.height/9
selectedCellHeight = CGFloat(unselectedCellHeight * 2.0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goBack() {
self.dismiss(animated: true, completion: nil)
}
func submitChanges() {
self.dismiss(animated: true) {
//
}
}
func addNewLanguage() {
numberOfLanguagesSpoken += 1
UIView.animate(withDuration: 0.5) {
//
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension LanguageViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfLanguagesSpoken
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Language", for: indexPath) as! LanguageProficiencyTableViewCell
cell.parentVC = self
cell.backgroundColor = .clear
cell.languages = self.languages
cell.proficiency = self.proficiency
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
return selectedCellHeight
} else {
return unselectedCellHeight
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
//
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedCellIndexPath != nil && selectedCellIndexPath == indexPath as IndexPath {
selectedCellIndexPath = nil
} else {
selectedCellIndexPath = indexPath as IndexPath
}
tableView.beginUpdates()
tableView.endUpdates()
if selectedCellIndexPath != nil {
// This ensures, that the cell is fully visible once expanded
tableView.scrollToRow(at: indexPath as IndexPath, at: .none, animated: true)
}
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return indexPath
}
}
import UIKit
class LanguageProficiencyTableViewCell: UITableViewCell {
let languageLabel = IndentedTextUILabel()
var languagePicker = UIPickerView()
weak var parentVC: LanguageViewController!
var cellHeight: CGFloat = 80
var open = false
var languages = [""]
var proficiency = [""]
var pickedLanguage = ""
var pickedProficiency = ""
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(languageLabel)
self.contentView.addSubview(languagePicker)
self.contentView.backgroundColor = .clear
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func layoutSubviews() {
let width = self.bounds.width
let height = cellHeight
let BGColor = UIColor.white.withAlphaComponent(0.1)
languageLabel.frame = CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0)
languageLabel.textColor = .white
languageLabel.backgroundColor = BGColor
languageLabel.text = "Language(Proficiency)"
languageLabel.leftInset = 25.0
let downArrow = UIButton()
let downArrowImage = UIImage(named: "arrow_drop_down")
downArrow.setImage(downArrowImage, for: .normal)
downArrow.center.y = height/2
downArrow.center.x = 4 * width/5
languageLabel.addSubview(downArrow)
let tap = UITapGestureRecognizer(target: self, action: #selector(cellTapped))
self.addGestureRecognizer(tap)
languagePicker.frame = CGRect(x: 0.0, y: self.cellHeight + 1.0, width: self.bounds.width, height: 0.0)
languagePicker.delegate = self
languagePicker.dataSource = self
languagePicker.backgroundColor = UIColor.white.withAlphaComponent(0.05)
languagePicker.selectRow(20, inComponent: 0, animated: true)
let backGroundView = UIView(frame: CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0))
backGroundView.backgroundColor = .clear
self.selectedBackgroundView = backGroundView
}
func cellTapped() {
UIView.animate(withDuration: 0.2, animations: {
if !self.open {
self.languagePicker.frame.size.height = self.cellHeight - 2.0
} else {
self.languagePicker.isHidden = true
}
}, completion: { (_) in
if self.open {
self.languagePicker.frame.size.height = 0.0
self.languagePicker.isHidden = false
}
self.open = !self.open
})
}
}
It will not work because the tap gesture is taking the tap on itself and not sending the tap to the views underneath it.
You can make the tap gesture send the tap to be passed to the background views by
mySwipeGesture.cancelsTouchesInView = false
as described in How to pass a 'tap' to UIButton that is underneath UIView with UISwipeGestureRecognizer?
Also if you are using tap gesture just to dismiss your keyboard you can do so in didSelectAt method of the tableView and do any other thing after dismissing the keyboard in that method.
I'm doing something like what you have done and it's working for me.
You have to add the gesture recognizer not inside the TableViewCell class but from your Controller class when you set the cell.
Like this:
class LanguageViewController: UIViewController {
#IBOutlet weak var titleLabel: AvenirUILabel!
#IBOutlet weak var backButton: UIButton!
#IBOutlet weak var submitButton: PinkUIButton!
#IBOutlet weak var addLanguageButton: UIButton!
#IBOutlet weak var addAnotherLanguageLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
var numberOfLanguagesSpoken = 2
var pickersStartHeight: CGFloat!
let downArrowImage = UIImage(named: "arrow_drop_down")
let BGColor = UIColor.white.withAlphaComponent(0.1)
var tag = 1001
var selectedCellHeight: CGFloat = 88.0
var unselectedCellHeight: CGFloat = 44.0
var selectedCellIndexPath: IndexPath?
let languages = ["Akan", "Amharic", "Arabic", "Assamese", "Awadhi", "Azerbaijani", "Balochi", "Belarusian", "Bengali", "Bhojpuri", "Burmese", "Cebuano", "Chewa", "Chhattisgarhi", "Chittagonian", "Czech", "Deccan", "Dhundhari", "Dutch", "Eastern Min", "English", "French", "Fula", "Gan Chinese", "German", "Greek", "Gujarati", "Haitian Creole", "Hakka", "Haryanvi", "Hausa", "Hiligaynon/Ilonggo", "Hindi ", "Hmong", "Hungarian", "Igbo", "Ilocano", "Italian", "Japanese", "Javanese", "Jin", "Kannada", "Kazakh", "Khmer", "Kinyarwanda", "Kirundi", "Konkani", "Korean", "Kurdish", "Madurese", "Magahi", "Maithili", "Malagasy", "Malay", "Malayalam", "Mandarin", "Marathi", "Marwari", "Mossi", "Nepali", "Northern Min", "Odia", "Oromo", "Pashto", "Persian", "Polish", "Portuguese", "Punjabi", "Quechua", "Romanian", "Russian", "Saraiki", "Serbo-Croatian", "Shona", "Sindhi", "Sinhalese", "Somali", "Southern Min", "Spanish", "Sundanese", "Swedish", "Sylheti", "Tagalog/Filipino", "Tamil", "Telugu", "Thai", "Turkish", "Turkmen", "Ukrainian", "Urdu", "Uyghur", "Uzbek", "Vietnamese", "Wu", "Xhosa", "Xiang ", "Yoruba", "Yue ", "Zhuang", "Zulu"]
var proficiency = ["Basic","Conversational","Proficient","Fluent","Native"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = backgroundTint
// initial set up
let height = self.view.bounds.height
titleLabel.textColor = .white
backButton.addTarget(self, action: #selector(goBack), for: .allTouchEvents)
tableView.dataSource = self
tableView.delegate = self
tableView.register(LanguageProficiencyTableViewCell.self, forCellReuseIdentifier: "Language")
tableView.backgroundColor = backgroundTint
tableView.allowsSelection = true
tableViewHeightConstraint.constant = 2*height/9
addLanguageButton.tintColor = .white
addLanguageButton.addTarget(self, action: #selector(addNewLanguage), for: .allTouchEvents)
addAnotherLanguageLabel.textColor = .white
submitButton.addTarget(self, action: #selector(submitChanges), for: .allEvents)
}
override func viewWillLayoutSubviews() {
unselectedCellHeight = self.view.bounds.height/9
selectedCellHeight = CGFloat(unselectedCellHeight * 2.0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goBack() {
self.dismiss(animated: true, completion: nil)
}
func submitChanges() {
self.dismiss(animated: true) {
//
}
}
func addNewLanguage() {
numberOfLanguagesSpoken += 1
UIView.animate(withDuration: 0.5) {
//
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension LanguageViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfLanguagesSpoken
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Language", for: indexPath) as! LanguageProficiencyTableViewCell
cell.parentVC = self
cell.backgroundColor = .clear
cell.languages = self.languages
cell.proficiency = self.proficiency
let tap = UITapGestureRecognizer(target: self, action: #selector("cellTapped:"))
cell.addGestureRecognizer(tap)
return cell
}
//Change here you can't call languagePicker with self but you have to get it from the object tapped that in your case is you cell
func cellTapped(recognizer: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.2, animations: {
if !self.open {
self.languagePicker.frame.size.height = self.cellHeight - 2.0
} else {
self.languagePicker.isHidden = true
}
}, completion: { (_) in
if self.open {
self.languagePicker.frame.size.height = 0.0
self.languagePicker.isHidden = false
}
self.open = !self.open
})
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
return selectedCellHeight
} else {
return unselectedCellHeight
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
//
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedCellIndexPath != nil && selectedCellIndexPath == indexPath as IndexPath {
selectedCellIndexPath = nil
} else {
selectedCellIndexPath = indexPath as IndexPath
}
tableView.beginUpdates()
tableView.endUpdates()
if selectedCellIndexPath != nil {
// This ensures, that the cell is fully visible once expanded
tableView.scrollToRow(at: indexPath as IndexPath, at: .none, animated: true)
}
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return indexPath
}
}
import UIKit
class LanguageProficiencyTableViewCell: UITableViewCell {
let languageLabel = IndentedTextUILabel()
var languagePicker = UIPickerView()
weak var parentVC: LanguageViewController!
var cellHeight: CGFloat = 80
var open = false
var languages = [""]
var proficiency = [""]
var pickedLanguage = ""
var pickedProficiency = ""
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(languageLabel)
self.contentView.addSubview(languagePicker)
self.contentView.backgroundColor = .clear
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func layoutSubviews() {
let width = self.bounds.width
let height = cellHeight
let BGColor = UIColor.white.withAlphaComponent(0.1)
languageLabel.frame = CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0)
languageLabel.textColor = .white
languageLabel.backgroundColor = BGColor
languageLabel.text = "Language(Proficiency)"
languageLabel.leftInset = 25.0
let downArrow = UIButton()
let downArrowImage = UIImage(named: "arrow_drop_down")
downArrow.setImage(downArrowImage, for: .normal)
downArrow.center.y = height/2
downArrow.center.x = 4 * width/5
languageLabel.addSubview(downArrow)
languagePicker.frame = CGRect(x: 0.0, y: self.cellHeight + 1.0, width: self.bounds.width, height: 0.0)
languagePicker.delegate = self
languagePicker.dataSource = self
languagePicker.backgroundColor = UIColor.white.withAlphaComponent(0.05)
languagePicker.selectRow(20, inComponent: 0, animated: true)
let backGroundView = UIView(frame: CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0))
backGroundView.backgroundColor = .clear
self.selectedBackgroundView = backGroundView
}
}