Struggling to adjust cell height with increase in content - ios

I have been struggling greatly almost regularly to be able to create a cell whose height adjusts with content while trying to do it programatically, some things i am trying are , image shows the problem
use below function
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
2)Use this in viewDidLoad
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
Setting bottom and top constraints to equal rather then not equal
Below i paste some code to show my struggle or trying every thing to get the cell to expand with content, which it does. not, can any one please suggest some ways to achieve it
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = restaurantMainTable.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
cell.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
view.addSubview(cell)
view.addSubview(cell.contentView)
view.addSubview(cell.restaurantName)
view.addSubview(cell.restaurantType)
view.addSubview(cell.restaurantLocation)
view.addSubview(cell.restaurantMiniImage)
view.addSubview(cell.restaurantHeartImage)
cell.restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantMiniImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantName.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantLocation.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantType.translatesAutoresizingMaskIntoConstraints = false
//Fonts
let font = UIFont(name: "Rubik-Medium", size: 18)
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [cell.restaurantName, cell.restaurantLocation, cell.restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font!)
}
let stackLabels = UIStackView()
view.addSubview(stackLabels)
stackLabels.alignment = .top
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(cell.restaurantName)
stackLabels.addArrangedSubview(cell.restaurantType)
stackLabels.addArrangedSubview(cell.restaurantLocation)
let stackImage = UIStackView()
view.addSubview(stackImage)
stackLabels.translatesAutoresizingMaskIntoConstraints = false
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
cell.restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.layer.cornerRadius = 30
cell.restaurantMiniImage.clipsToBounds = true
cell.restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -10).isActive = true
cell.restaurantHeartImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 20).isActive = true
stackImage.addArrangedSubview(cell.restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
view.addSubview(stackImage)
stackImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: 10).isActive = true
stackImage.topAnchor.constraint(greaterThanOrEqualTo: cell.topAnchor, constant: 10).isActive = true
// stackImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 10).isActive = true
stackImage.trailingAnchor.constraint(equalTo: cell.restaurantHeartImage.leadingAnchor, constant: -10).isActive = true
// stackImage.bottomAnchor.constraint(equalTo: cell.bottomAnchor, constant: -10).isActive = true
stackImage.bottomAnchor.constraint(greaterThanOrEqualTo: cell.bottomAnchor, constant: -10).isActive = true
cell.restaurantName.text = restaurants[indexPath.row].name
cell.restaurantType.text = restaurants[indexPath.row].type
cell.restaurantLocation.text = restaurants[indexPath.row].location
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
if let restaurantImage = restaurants[indexPath.row].image {
cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
}
return cell
default:
fatalError("no data found")
}
}
UPDATE - The whole class
//
// RestaurantMainController.swift
// LaVivaRepeat
//
//
import UIKit
import CoreData
class RestaurantMainController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var restaurants: [Restaurant] = []
var fetchResultController: NSFetchedResultsController<Restaurant>!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurants.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
let restaurantMainTable = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(restaurantMainTable)
//MARK:- add delegates as self, always, else no contact with model will take place
restaurantMainTable.rowHeight = UITableView.automaticDimension
restaurantMainTable.estimatedRowHeight = 60
self.restaurantMainTable.delegate = self
self.restaurantMainTable.dataSource = self
self.restaurantMainTable.separatorStyle = .singleLine
//MARK:- create a view to show when no records are there
let backGroundView = UIView()
view.addSubview(backGroundView)
backGroundView.heightAnchor.constraint(equalToConstant: 500).isActive = true
let backGroundImage = UIImageView()
backGroundImage.translatesAutoresizingMaskIntoConstraints = false
backGroundView.translatesAutoresizingMaskIntoConstraints = false
backGroundImage.heightAnchor.constraint(equalToConstant: 300).isActive = true
backGroundImage.widthAnchor.constraint(equalToConstant: 320).isActive = true
backGroundImage.image = UIImage(named: "empty")
backGroundView.addSubview(backGroundImage)
backGroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
backGroundView.topAnchor.constraint(equalTo: view.topAnchor, constant: 90).isActive = true
backGroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20).isActive = true
restaurantMainTable.backgroundView = backGroundView
restaurantMainTable.backgroundView?.isHidden = true
//MARK:- Add constraints to table
self.restaurantMainTable.translatesAutoresizingMaskIntoConstraints = false
self.restaurantMainTable.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
self.restaurantMainTable.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
self.restaurantMainTable.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
self.restaurantMainTable.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
//MARK:- register RestaurantMainViewCells
self.restaurantMainTable.register(RestaurantMainViewCells.self, forCellReuseIdentifier: String(describing: RestaurantMainViewCells.self))
//MARK:- Get fetch request
let fetchRequest: NSFetchRequest<Restaurant> = Restaurant.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
if let fetchObject = fetchResultController.fetchedObjects {
restaurants = fetchObject
}
}
catch {
print(error)
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
restaurantMainTable.rowHeight = UITableView.automaticDimension
switch indexPath.row {
case 0:
let cell = restaurantMainTable.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
// cell.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
view.addSubview(cell)
view.addSubview(cell.contentView)
view.addSubview(cell.restaurantName)
view.addSubview(cell.restaurantType)
view.addSubview(cell.restaurantLocation)
view.addSubview(cell.restaurantMiniImage)
view.addSubview(cell.restaurantHeartImage)
cell.restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantMiniImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantName.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantLocation.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantType.translatesAutoresizingMaskIntoConstraints = false
//Fonts
let font = UIFont(name: "Rubik-Medium", size: 18)
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [cell.restaurantName, cell.restaurantLocation, cell.restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font!)
}
let stackLabels = UIStackView()
view.addSubview(stackLabels)
stackLabels.alignment = .top
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(cell.restaurantName)
stackLabels.addArrangedSubview(cell.restaurantType)
stackLabels.addArrangedSubview(cell.restaurantLocation)
let stackImage = UIStackView()
view.addSubview(stackImage)
stackLabels.translatesAutoresizingMaskIntoConstraints = false
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
cell.restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.layer.cornerRadius = 30
cell.restaurantMiniImage.clipsToBounds = true
cell.restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -10).isActive = true
cell.restaurantHeartImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 20).isActive = true
stackImage.addArrangedSubview(cell.restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
view.addSubview(stackImage)
stackImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: 10).isActive = true
stackImage.topAnchor.constraint(greaterThanOrEqualTo: cell.topAnchor, constant: 10).isActive = true
// stackImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 10).isActive = true
stackImage.trailingAnchor.constraint(equalTo: cell.restaurantHeartImage.leadingAnchor, constant: -10).isActive = true
// stackImage.bottomAnchor.constraint(equalTo: cell.bottomAnchor, constant: -10).isActive = true
stackImage.bottomAnchor.constraint(greaterThanOrEqualTo: cell.bottomAnchor, constant: -10).isActive = true
cell.restaurantName.text = restaurants[indexPath.row].name
cell.restaurantType.text = restaurants[indexPath.row].type
cell.restaurantLocation.text = restaurants[indexPath.row].location
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
if let restaurantImage = restaurants[indexPath.row].image {
cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
}
return cell
default:
fatalError("no data found")
}
}
//MARK:- Make custom navigation bar large font size and use rubik fonts
override func viewWillAppear(_ animated: Bool) {
navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.topItem?.title = "LaViva Hotel"
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.hidesBarsOnSwipe = true
if let customFont = UIFont(name: "Rubik-Medium", size: 40) {
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(red: 200/255, green: 70/255, blue: 70/255, alpha: 1), NSAttributedString.Key.font: customFont]
}
//MARK:- for empty table
if restaurants.count > 0 {
self.restaurantMainTable.backgroundView?.isHidden = true
self.restaurantMainTable.separatorStyle = .singleLine
}
else {
self.restaurantMainTable.backgroundView?.isHidden = false
self.restaurantMainTable.separatorStyle = .none
}
//MARK:- make an + button appear on top left
let addButton = UIBarButtonItem(image: UIImage(named: "plus"), style: .plain, target: self, action: #selector(addNewRestaurant))
//navigationController?.navigationItem.rightBarButtonItem = addButton
self.navigationItem.rightBarButtonItem = addButton
}
//MARK:- addNewRestaurant function
#objc func addNewRestaurant() {
let pushController = RestaurantAddController()
navigationController?.pushViewController(pushController, animated: true)
}
//MARK:- try and show cell and tower as default or dark done here
override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
//add update delete
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
restaurantMainTable.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
restaurantMainTable.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
restaurantMainTable.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath {
restaurantMainTable.reloadRows(at: [indexPath], with: .fade)
}
default:
restaurantMainTable.reloadData()
}
if let fetchedObjects = controller.fetchedObjects {
restaurants = fetchedObjects as! [Restaurant]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
restaurantMainTable.endUpdates()
}
//MARK:- left swipr delete
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completionHandler) in
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
let restaurantsToDelete = self.fetchResultController.object(at: indexPath)
context.delete(restaurantsToDelete)
appDelegate.saveContext()
}
completionHandler(true)
}
let swipeConfiguration: UISwipeActionsConfiguration
swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction])
return swipeConfiguration
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
UPDATE
import UIKit
class RestaurantMainViewCells: UITableViewCell {
var restaurantMiniImage = UIImageView()
var restaurantHeartImage = UIImageView()
var restaurantName = UILabel()
var restaurantType = UILabel()
var restaurantLocation = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

All of your cell setup - adding and constraining UI elements - should be done in your cell class. Absolutely NOT in cellForRowAt.
You would do well to go through a few tutorials on creating dynamic cells.
But, to give you an idea, here is your code modified so you can see what's happening:
struct Restaurant {
var name: String = ""
var type: String = ""
var location: String = ""
// however you have your image information stored
//var image
}
class RestaurantMainViewCells: UITableViewCell {
var restaurantMiniImage = UIImageView()
var restaurantHeartImage = UIImageView()
var restaurantName = UILabel()
var restaurantType = UILabel()
var restaurantLocation = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() -> Void {
// so we can see the image view frames without actual images...
restaurantMiniImage.backgroundColor = .green
restaurantHeartImage.backgroundColor = .red
var font: UIFont = UIFont.systemFont(ofSize: 18)
if let f = UIFont(name: "Rubik-Medium", size: 18) {
font = f
}
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [restaurantName, restaurantLocation, restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font)
// so we can see label frames...
label.backgroundColor = .yellow
}
let stackLabels = UIStackView()
stackLabels.alignment = .fill
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(restaurantName)
stackLabels.addArrangedSubview(restaurantType)
stackLabels.addArrangedSubview(restaurantLocation)
let stackImage = UIStackView()
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
restaurantMiniImage.layer.cornerRadius = 30
restaurantMiniImage.clipsToBounds = true
stackImage.addArrangedSubview(restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
contentView.addSubview(stackImage)
contentView.addSubview(restaurantHeartImage)
restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// mini image 60x60
restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60),
restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60),
// heart image 20 x 20
restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20),
restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20),
// heart image top+20 trailing-10
restaurantHeartImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
restaurantHeartImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
// horizontal stack top / leading / bottom and trailinh to heart image
// all with 10-pts "padding"
stackImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
stackImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
stackImage.trailingAnchor.constraint(equalTo: restaurantHeartImage.leadingAnchor, constant: -10),
stackImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
])
}
}
class RestaurantMainController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var restaurants: [Restaurant] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurants.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
let restaurantMainTable = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(restaurantMainTable)
//MARK:- add delegates as self, always, else no contact with model will take place
restaurantMainTable.estimatedRowHeight = 60
self.restaurantMainTable.delegate = self
self.restaurantMainTable.dataSource = self
self.restaurantMainTable.separatorStyle = .singleLine
//MARK:- Add constraints to table
self.restaurantMainTable.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
restaurantMainTable.topAnchor.constraint(equalTo: view.topAnchor),
restaurantMainTable.bottomAnchor.constraint(equalTo: view.bottomAnchor),
restaurantMainTable.leadingAnchor.constraint(equalTo: view.leadingAnchor),
restaurantMainTable.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
//MARK:- register RestaurantMainViewCells
self.restaurantMainTable.register(RestaurantMainViewCells.self, forCellReuseIdentifier: String(describing: RestaurantMainViewCells.self))
//MARK:- Get fetch request
// I don't have your "fetch" data, so I'm just adding a couple restaurants here
restaurants.append(Restaurant(name: "Cafe De Loir", type: "Chinese Cousine", location: "Hong Kong"))
restaurants.append(Restaurant(name: "Bob's Cafe", type: "Japanese Cousine", location: "Tokyo"))
restaurants.append(Restaurant(name: "Mary's Restaurant", type: "Home Cooking", location: "Dallas, Texas"))
// let fetchRequest: NSFetchRequest<Restaurant> = Restaurant.fetchRequest()
// let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
// fetchRequest.sortDescriptors = [sortDescriptor]
//
// if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
// let context = appDelegate.persistentContainer.viewContext
// fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
// fetchResultController.delegate = self
//
// do {
// try fetchResultController.performFetch()
// if let fetchObject = fetchResultController.fetchedObjects {
// restaurants = fetchObject
// }
// }
//
// catch {
// print(error)
// }
// }
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
let r = restaurants[indexPath.row]
cell.restaurantName.text = r.name
cell.restaurantType.text = r.type
cell.restaurantLocation.text = r.location
//if let restaurantImage = restaurants[indexPath.row].image {
// cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
//}
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
return cell
}
}
The result (I don't have your images so the image views have green or red background color):

Related

How to display an icon/image on uicollectionviewcell after it has been selected

I have a uiCollectionViewCell which loads image from an api. I want to display another image/icon on the cell when a user clicks on it. In my custom cell I have two images one which display the image from the URL and the second one is the one I would like to show if the user has clicked on it. I'm doing this to alert the user that they have selected that cell. Below is my sample code
protocol ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String)
}
class GuestRateMovieView: UIViewController, ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) {
self.userChoice = userChoice
totalRated = totalRated + 1
lblRated.text = "\(totalRated) rated"
if totalRated > 0 {
ratedView.backgroundColor = .ratedGoldColour
}else{
ratedView.backgroundColor = .white
}
if totalRated >= 5 {
btnFloatNext.alpha = 1
}
if totalRated > 5 {
userChoiceMovieImage.sd_setImage(with: URL(string: rateImageUrl), placeholderImage: UIImage(named: "ImagePlaceholder"))
lblUserChoice.text = "Great taste. We love the \(title) too."
}
var rating = 1
if userChoice == "Hate it"{
rating = 1
}else if userChoice == "Good" {
rating = 3
}else{
rating = 5
}
let guestRatingValues = GuestUserRate(id: rateMovieID, imageUrl: rateImageUrl, userRate: rating)
GuestRateMovieView.createUserRating(guestRatingValues) =>", rateImageUrl)
print("Received on movie", totalRated)
}
func getMovieDetails(){
activityLoader.displayActivityLoader(image: activityLoader, view: activityLoaderView)
let urlString = "https://t2fmmm2hfg.execute-api.eu-west-2.amazonaws.com/mobile/media/onboarding-items"
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postParameters: Dictionary<String, Any> = [
"category": "tv"
]
if let postData = (try? JSONSerialization.data(withJSONObject: postParameters, options: JSONSerialization.WritingOptions.prettyPrinted)){
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
guard let data = data, error == nil else {
return
}
do {
print("got data")
let jsonResult = try JSONDecoder().decode([Responder].self, from: data)
DispatchQueue.main.async {
self?.movieObj = jsonResult
self?.moviesCollectionView.reloadData()
self?.activityLoader.removeActivityLoader(image: self!.activityLoader, view: self!.activityLoaderView)
}
// jsonResult.forEach { course in print(course.type) }
}catch {
print(error)
}
}
task.resume()
}
}
var movieObj: [Responder] = []
override func viewDidLoad() {
super.viewDidLoad()
getMovieDetails()
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return movieObj.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.movieImage.image = nil
cell.configure(with: movieObj[indexPath.row].packShot?.thumbnail ?? ""
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let modalVC = RateSingleMovieView()
modalVC.movieID = movieObj[indexPath.row].id
modalVC.movieTitle = movieObj[indexPath.row].title
modalVC.movieImageURL = movieObj[indexPath.row].packShot?.thumbnail ?? ""
modalVC.delegate = self
modalVC.modalPresentationStyle = .overCurrentContext
modalVC.modalTransitionStyle = .crossDissolve
present(modalVC, animated: true, completion: nil)
}
class MoviesCollectionCell: UICollectionViewCell {
private var movieImages = NSCache<NSString, NSData>()
weak var textLabel: UILabel!
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
return image
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
image.alpha = 0
return image
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
movieImage.addSubview(btnRate)
NSLayoutConstraint.activate([
movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
btnRate.widthAnchor.constraint(equalToConstant: 30),
btnRate.heightAnchor.constraint(equalToConstant: 30)
])
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
btnRate.image = nil
}
func configure(with urlString: String, ratingObj: MovieRating){
movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now in my modal where the user will rate. In my case I'm using swipe gesture for the rating
class RateSingleMovieView: UIViewController, ModalDelegate, ModalSearchDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) {
self.userChoice = userChoice
totalRated = totalRated + 1
}
var delegate: ModalDelegate?
override func viewDidLoad() {
super.viewDidLoad()
redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .up))redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .left))redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .right))
}
#objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {
switch sender.direction {
case .up:
showUserRatingSelection(userChoice: "Good")
case .left:
showUserRatingSelection(userChoice: "Hate it")
case .right:
showUserRatingSelection(userChoice: "Love it")
default:
break
}
}
#objc private func removeModal(){
dismiss(animated: true, completion: nil)
}
private func showUserRatingSelection(userChoice: String){
self.hateItView.alpha = 1
if userChoice == "Hate it"{
userChoiceEmoji.image = UIImage(named: "HateIt")
lblRate.text = "Hate it"
}else if userChoice == "Good" {
userChoiceEmoji.image = UIImage(named: "goodRate")
lblRate.text = "Good"
}else{
userChoiceEmoji.image = UIImage(named: "LoveIt")
lblRate.text = "Love it"
}
userChoiceEmoji.alpha = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("hello ", userChoice)
self.delegate?.changeValue(userChoice: userChoice, rateMovieID: self.movieID!, rateImageUrl: self.movieImageURL!, title: self.movieTitle!)
self.removeModal()
}
}
}
I am able to use the delegate here to send info back to GuestRateMovieView Controller and update a label there. Now my only problem is displaying the icon on the selected cell with the user choice.
First note... setup your constraints in init -- Absolutely NOT in layoutSubviews().
Edit -- forget everything else previously here, because it had nothing to do with what you're actually trying to accomplish.
New Answer
To clarify your goal:
display a collection view of objects - in this case, movies
when the user selects a cell, show a "Rate This Movie" view
when the user selects a Rating (hate, good, love), save that rating and update the cell with a "Rating Image"
So, the first thing you need is a data structure that includes a "rating" value. Let's use an enum for the rating itself:
enum MovieRating: Int {
case none, hate, good, love
}
Then we might have a "Movie Object" like this:
struct MovieObject {
var title: String = ""
var urlString: String = ""
var rating: MovieRating = .none
// maybe some other properties
}
For our data, we'll have an Array of MovieObject. When we configure each cell (in cellForItemAt), we need to set the Movie Image and the Rating Image.
So, your cell class may have this:
func configure(with movieObj: MovieObject) {
movieImage.sd_setImage(with: URL(string: movieObj.urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
switch movieObj.rating {
case .hate:
if let img = UIImage(systemName: "hand.thumbsdown") {
btnRate.image = img
}
case .good:
if let img = UIImage(systemName: "face.smiling") {
btnRate.image = img
}
case .love:
if let img = UIImage(systemName: "hand.thumbsup") {
btnRate.image = img
}
default:
btnRate.image = nil
}
}
and your cellForItemAt would look like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! MoviesCollectionCell
c.configure(with: moviesArray[indexPath.row])
return c
}
When the user selects a cell, we can present a "Rate This Movie" view controller - which will have buttons for Hate / Good / Love.
If the user taps one of those buttons, we can use a closure to update the data and reload that cell:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RateTheMovieVC()
vc.movieObj = moviesArray[indexPath.item]
vc.callback = { [weak self] rating in
guard let self = self else { return }
// update the data
self.moviesArray[indexPath.item].rating = rating
// reload the cell
self.collectionView.reloadItems(at: [indexPath])
// dismiss the RateTheMovie view controller
self.dismiss(animated: true)
}
// present the RateTheMovie view controller
present(vc, animated: true)
}
Here's a complete example... I don't have your data (movie names, images, etc), so we'll use an array of "Movie Titles" from A to Z, and the cells will look like this:
and so on.
enum and struct
enum MovieRating: Int {
case none, hate, good, love
}
struct MovieObject {
var title: String = ""
var urlString: String = ""
var rating: MovieRating = .none
}
collection view cell
class MoviesCollectionCell: UICollectionViewCell {
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
image.backgroundColor = .blue
return image
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
return image
}()
// we don't have Movie Images for this example, so
// we'll use some labels for the Movie Title
var labels: [UILabel] = []
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
for _ in 0..<4 {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.textColor = .cyan
if let f = UIFont(name: "TimesNewRomanPS-BoldMT", size: 60) {
v.font = f
}
contentView.addSubview(v)
labels.append(v)
}
// stack views for the labels
let stTop = UIStackView()
stTop.axis = .horizontal
stTop.distribution = .fillEqually
stTop.addArrangedSubview(labels[0])
stTop.addArrangedSubview(labels[1])
let stBot = UIStackView()
stBot.axis = .horizontal
stBot.distribution = .fillEqually
stBot.addArrangedSubview(labels[2])
stBot.addArrangedSubview(labels[3])
let st = UIStackView()
st.axis = .vertical
st.distribution = .fillEqually
st.addArrangedSubview(stTop)
st.addArrangedSubview(stBot)
st.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(st)
contentView.addSubview(btnRate)
// setup constriaints here
NSLayoutConstraint.activate([
movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
st.topAnchor.constraint(equalTo: movieImage.topAnchor),
st.leadingAnchor.constraint(equalTo: movieImage.leadingAnchor),
st.trailingAnchor.constraint(equalTo: movieImage.trailingAnchor),
st.bottomAnchor.constraint(equalTo: movieImage.bottomAnchor),
btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
btnRate.widthAnchor.constraint(equalToConstant: 40),
btnRate.heightAnchor.constraint(equalToConstant: 40)
])
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
}
func configure(with movieObj: MovieObject) {
// I don't have your cell images, or the "sd_setImage" function
// un-comment the next line to set your images
// movieImage.sd_setImage(with: URL(string: movieObj.urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
labels.forEach { v in
v.text = movieObj.title
}
switch movieObj.rating {
case .hate:
if let img = UIImage(systemName: "hand.thumbsdown") {
btnRate.image = img
}
case .good:
if let img = UIImage(systemName: "face.smiling") {
btnRate.image = img
}
case .love:
if let img = UIImage(systemName: "hand.thumbsup") {
btnRate.image = img
}
default:
btnRate.image = nil
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
example view controller
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
var moviesArray: [MovieObject] = []
override func viewDidLoad() {
super.viewDidLoad()
let fl = UICollectionViewFlowLayout()
fl.itemSize = CGSize(width: 100.0, height: 200.0)
fl.scrollDirection = .vertical
fl.minimumLineSpacing = 8
fl.minimumInteritemSpacing = 8
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
collectionView.register(MoviesCollectionCell.self, forCellWithReuseIdentifier: "c")
collectionView.dataSource = self
collectionView.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// let's change the collection view cell size to fit
// two "columns"
if let fl = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
fl.itemSize = CGSize(width: (collectionView.frame.width - fl.minimumInteritemSpacing) * 0.5, height: 200.0)
}
simulateGettingData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return moviesArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! MoviesCollectionCell
c.configure(with: moviesArray[indexPath.row])
return c
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RateTheMovieVC()
vc.movieObj = moviesArray[indexPath.item]
vc.callback = { [weak self] rating in
guard let self = self else { return }
// update the data
self.moviesArray[indexPath.item].rating = rating
// reload the cell
self.collectionView.reloadItems(at: [indexPath])
// dismiss the RateTheMovie view controller
self.dismiss(animated: true)
}
// present the RateTheMovie view controller
present(vc, animated: true)
}
func simulateGettingData() {
// let's just create an array of MovieObject
// where each Title will be a letter from A to Z
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".forEach { c in
let m = MovieObject(title: String(c), urlString: "", rating: .none)
moviesArray.append(m)
}
collectionView.reloadData()
}
}
example "Rate The Movie" view controller
class RateTheMovieVC: UIViewController {
// this will be used to tell the presenting controller
// that a rating button was selected
var callback: ((MovieRating) -> ())?
var movieObj: MovieObject!
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
image.backgroundColor = .systemBlue
return image
}()
// we don't have Movie Images for this example, so
// we'll use a label for the "Movie Title"
let titleLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
v.textAlignment = .center
v.textColor = .yellow
v.font = .systemFont(ofSize: 240, weight: .bold)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
// let's add 3 "rate" buttons near the bottom
let btnHate = UIButton()
let btnGood = UIButton()
let btnLove = UIButton()
let btns: [UIButton] = [btnHate, btnGood, btnLove]
let names: [String] = ["hand.thumbsdown", "face.smiling", "hand.thumbsup"]
for (b, s) in zip(btns, names) {
b.backgroundColor = .systemRed
b.layer.cornerRadius = 8
b.layer.masksToBounds = true
if let img = UIImage(systemName: s, withConfiguration: UIImage.SymbolConfiguration(pointSize: 32)) {
b.setImage(img, for: [])
}
b.tintColor = .white
b.heightAnchor.constraint(equalToConstant: 60.0).isActive = true
}
let btnStack = UIStackView()
btnStack.spacing = 20
btnStack.distribution = .fillEqually
btns.forEach { b in
btnStack.addArrangedSubview(b)
}
view.addSubview(movieImage)
view.addSubview(titleLabel)
btnStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnStack)
// setup constriaints here
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btnStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btnStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
btnStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
titleLabel.topAnchor.constraint(equalTo: movieImage.topAnchor, constant: 8.0),
titleLabel.leadingAnchor.constraint(equalTo: movieImage.leadingAnchor, constant: 12.0),
titleLabel.trailingAnchor.constraint(equalTo: movieImage.trailingAnchor, constant: -12.0),
titleLabel.bottomAnchor.constraint(equalTo: movieImage.bottomAnchor, constant: -8.0),
movieImage.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
movieImage.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
movieImage.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
movieImage.bottomAnchor.constraint(equalTo: btnStack.topAnchor, constant: -20.0),
])
// here we would set the movie image
// set the title label text, since we don't have images right now
titleLabel.text = movieObj.title
btnHate.addTarget(self, action: #selector(hateTap(_:)), for: .touchUpInside)
btnGood.addTarget(self, action: #selector(goodTap(_:)), for: .touchUpInside)
btnLove.addTarget(self, action: #selector(loveTap(_:)), for: .touchUpInside)
}
#objc func hateTap(_ sender: UIButton) {
callback?(.hate)
}
#objc func goodTap(_ sender: UIButton) {
callback?(.good)
}
#objc func loveTap(_ sender: UIButton) {
callback?(.love)
}
}
It will look like this on launch:
Then we select the first cell and we see this:
We select the "Thumbs Up" button, and we see this:
Then scroll down and select-and-rate a few other cells:
You mention in a comment a "cache DB" ... assuming that will be persistent data, it's up to you to store the user-selected Rating.
in your MoviesCollectionCell file put function like this
func loadImageAfterClickingCell() {
// TODO: check this cell is already clicked and already download the image like if yourImageView == nil or not nil so you can add guard like guard yourImageView.image == nil else { return } similar to this
guard let preparedUrl = URL(string: urlString) else { return }
yourImageView.alpha = 1
yourImageView.sd_setImage(with: preparedUrl, placeholderImage: UIImage(named: "ImagePlaceholder"))
}
And after that in didSelectItemAt
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.loadImageAfterClickingCell()
}
A simple method injection like this must save you as you want.

trouble with constructs after adding view to contentView

I need to add a target to an image in a tableview, but it doesn't work if I don't add a view to the contentView. After that, my constraints broke.
https://github.com/termyter/stackoverflow-search
beforeenter image description here
afterenter image description here
PostController
import Foundation
import UIKit
protocol AnswerhNetworkDelegate: AnyObject {
func getListModels(noteModels: [AnswerModel])
}
class PostController: UIViewController, UITableViewDelegate, UITableViewDataSource, AnswerhNetworkDelegate{
func getListModels(noteModels: [AnswerModel]) {
listModels += noteModels
table.reloadData()
}
private var listModels: [AnswerModel] = []
private var searchText = UITextField()
private var searchButton = UIButton()
private var table = UITableView()
private let answerNetwork = AnswerNetwork()
var model: PostModel = PostModel.empty
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
table.register(PostCell.self, forCellReuseIdentifier: "PostCell")
table.register(CustomAnswerCell.self, forCellReuseIdentifier: "CustomAnswerCell")
table.delegate = self
table.dataSource = self
table.estimatedRowHeight = 68.0
table.rowHeight = UITableView.automaticDimension
setupUI()
answerNetwork.answerNetworkDelegate = self
//переделать
answerNetwork.fetch(idPost: model.id)
}
override func viewDidAppear(_ animated: Bool) {
table.reloadData()
}
private func setupUI() {
table.translatesAutoresizingMaskIntoConstraints = false
table.separatorStyle = .none
view.addSubview(table)
table.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
table.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
table.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
table.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
listModels.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as? PostCell else {
fatalError("не CustomCell")
}
// cell.cellView.image.userInteractionEnabled =
let lpgr = UITapGestureRecognizer(target: self, action: #selector(PostController.handleTapPress(_:)))
cell.cellView.image.isUserInteractionEnabled = true
cell.cellView.image.addGestureRecognizer(lpgr)
cell.selectionStyle = .none
cell.model = model
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomAnswerCell", for: indexPath) as? CustomAnswerCell else {
fatalError("не CustomCell")
}
cell.selectionStyle = .none
cell.model = listModels[indexPath.row - 1]
return cell
}
}
#objc func handleTapPress(_ sender: Any){
UIApplication.shared.openURL(URL(string: model.link)!)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
extension String {
init?(htmlEncodedString: String) {
guard let data = htmlEncodedString.data(using: .utf8) else {
return nil
}
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
return nil
}
self.init(attributedString.string)
}
}
PostCell
import Foundation
import UIKit
class PostCell: UITableViewCell {
var cellView = PostView()
private var selectedButton = UIButton(type: .custom)
var model: PostModel? {
get {
cellView.model
}
set {
cellView.model = newValue ?? PostModel.empty
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
setupView()
// print(cellView.frame)
// print(contentView.frame)
// print(self.frame)
}
override func prepareForReuse() {
super.prepareForReuse()
isHidden = false
isSelected = false
isHighlighted = false
self.model = PostModel.empty
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
cellView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(cellView)
// addSubview(cellView)
cellView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor).isActive = true
cellView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor).isActive = true
cellView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
cellView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -5).isActive = true
}
}
PostView is in the PostCell as a cell and is injected with the Model
import Foundation
import UIKit
class PostView: UIView {
private var titleText = UILabel()
private var bodyText = UILabel()
private var nameText = UILabel()
var image = UIImageView()
private var dateText = UILabel()
private var answerCount = UILabel()
var model: PostModel = PostModel.empty {
didSet {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.YYY"
titleText.text = model.title
image.load(urlString: model.image)
nameText.text = "Автор:" + model.name
dateText.text = "Дата: " + formatter.string(from: model.date)
answerCount.text = "Кол-во ответов: " + String(model.answer_count)
bodyText.text = model.body
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .systemBackground
setupTitleText()
setupImage()
let tapGR = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped))
image.addGestureRecognizer(tapGR)
image.isUserInteractionEnabled = true
setupNameText()
setupDateText()
setupAnswerCount()
setupBodyText()
}
#objc func imageTapped(sender: UITapGestureRecognizer) {
if sender.state == .ended {
UIApplication.shared.openURL(URL(string: model.link)!)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupTitleText() {
titleText.translatesAutoresizingMaskIntoConstraints = false
titleText.numberOfLines = 0
let maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
let expectedLabelSize: CGSize = titleText.sizeThatFits(maximumLabelSize)
var newFrame: CGRect = titleText.frame
newFrame.size.height = expectedLabelSize.height
titleText.frame = newFrame
addSubview(titleText)
titleText.topAnchor.constraint(equalTo: topAnchor, constant: 5).isActive = true
titleText.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 10).isActive = true
titleText.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10).isActive = true
}
private func setupImage() {
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
addSubview(image)
image.topAnchor.constraint(equalTo: titleText.bottomAnchor, constant: 5).isActive = true
image.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 10).isActive = true
image.trailingAnchor.constraint(equalTo: leadingAnchor, constant: 60).isActive = true
//image.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
}
private func setupNameText() {
nameText.translatesAutoresizingMaskIntoConstraints = false
addSubview(nameText)
nameText.topAnchor.constraint(equalTo: titleText.bottomAnchor, constant: 5).isActive = true
nameText.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
nameText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
private func setupDateText() {
dateText.translatesAutoresizingMaskIntoConstraints = false
addSubview(dateText)
dateText.topAnchor.constraint(equalTo: nameText.bottomAnchor, constant: 5).isActive = true
dateText.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
dateText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
private func setupAnswerCount() {
answerCount.translatesAutoresizingMaskIntoConstraints = false
addSubview(answerCount)
answerCount.topAnchor.constraint(equalTo: dateText.bottomAnchor, constant: 5).isActive = true
answerCount.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
answerCount.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
// answerCount.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
}
private func setupBodyText() {
bodyText.translatesAutoresizingMaskIntoConstraints = false
bodyText.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bodyText.translatesAutoresizingMaskIntoConstraints = false
bodyText.numberOfLines = 0
let maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
let expectedLabelSize: CGSize = bodyText.sizeThatFits(maximumLabelSize)
// create a frame that is filled with the UILabel frame data
var newFrame: CGRect = bodyText.frame
// resizing the frame to calculated size
newFrame.size.height = expectedLabelSize.height
// put calculated frame into UILabel frame
bodyText.frame = newFrame
addSubview(bodyText)
bodyText.topAnchor.constraint(equalTo: answerCount.bottomAnchor, constant: 5).isActive = true
bodyText.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 5).isActive = true
bodyText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5).isActive = true
bodyText.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
}
}
on this github, I looked at how he arranges the constraints and made it for himself https://github.com/jeantimex/ios-swift-collapsible-table-section/blob/master/ios-swift-collapsible-table-section/CollapsibleTableViewCell.swift
and redid PostCell
private func setupView() {
let marginGuide = contentView.layoutMarginsGuide
cellView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(cellView)
cellView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
cellView.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
cellView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5).isActive = true
cellView.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
}

Passing Data from one ViewController to Another Programmatically, Swift [duplicate]

This question already has answers here:
How to pass data from modal view controller back when dismissed
(4 answers)
Closed 2 years ago.
I just want to transfer some data between two ViewControllers and also wrote some Code. But, if I update my tableView, the user-given data isn't presented at the tableView. Can anyone help me?
import UIKit
import CoreData
class AddNewRecipeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let reuseIdentifer = "IngredientCell"
let pressGesture = UILongPressGestureRecognizer()
let titleLabel = UILabel()
let tf_one = UITextField()
let tf_two = UITextField()
let tableView = UITableView()
let submitButton = UIButton()
let newButton = UIButton()
var name_array = [String]()
var unit_array = [String]()
var quantity_array = [String]()
let recipeId = Int.random(in: 0...5000)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createViewControllerItems()
}
#objc func refresh(){
print(name_array)
self.tableView.reloadData()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
func createViewControllerItems() {
// - MARK: Titel
titleLabel.text = "Neues Rezept"
titleLabel.textColor = .black
titleLabel.textAlignment = .center
titleLabel.font = UIFont(name: "Bradley Hand", size: 24)
view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
newButton.setTitle("test", for: UIControl.State.normal)
newButton.setTitleColor(.black, for: UIControl.State.normal)
newButton.addTarget(self, action: #selector(refresh), for: .touchUpInside)
view.addSubview(newButton)
newButton.translatesAutoresizingMaskIntoConstraints = false
newButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true
newButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
newButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
newButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Überschrift
tf_one.placeholder = "Name"
tf_one.borderStyle = .roundedRect
tf_one.keyboardType = .default
tf_one.spellCheckingType = .yes
tf_one.smartInsertDeleteType = .yes
tf_one.autocorrectionType = .yes
tf_one.autocapitalizationType = .sentences
view.addSubview(tf_one)
tf_one.translatesAutoresizingMaskIntoConstraints = false
tf_one.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15).isActive = true
tf_one.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_one.widthAnchor.constraint(equalToConstant: 270).isActive = true
tf_one.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Anzahl Portionen
tf_two.placeholder = "Anzahl"
tf_two.borderStyle = .roundedRect
tf_two.keyboardType = .decimalPad
view.addSubview(tf_two)
tf_two.translatesAutoresizingMaskIntoConstraints = false
tf_two.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_two.leftAnchor.constraint(equalTo: tf_one.rightAnchor, constant: 3).isActive = true
tf_two.widthAnchor.constraint(equalToConstant: 70).isActive = true
tf_two.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Table View
tableView.delegate = self
tableView.dataSource = self
tableView.register(IngredientCell.self, forCellReuseIdentifier: reuseIdentifer)
tableView.rowHeight = 55
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
tableView.topAnchor.constraint(equalTo: tf_one.bottomAnchor, constant: 10).isActive = true
// - MARK: Bestätigen
submitButton.layer.cornerRadius = 20
submitButton.clipsToBounds = true
submitButton.backgroundColor = .lightGray
submitButton.setTitle("ok", for: UIControl.State.normal)
submitButton.setTitleColor(.white, for: UIControl.State.normal)
submitButton.titleLabel?.font = UIFont(name: "Chalkduster", size: 24)
submitButton.addTarget(self, action: #selector(save), for: .touchUpInside)
view.addSubview(submitButton)
submitButton.translatesAutoresizingMaskIntoConstraints = false
submitButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true
submitButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
submitButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
submitButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Add Gesture
pressGesture.addTarget(self, action: #selector(pressAction))
view.addGestureRecognizer(pressGesture)
}
#objc func pressAction() {
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
let vc: AddNewIngredientViewController!
vc = AddNewIngredientViewController()
vc.modalPresentationStyle = .automatic
self.present(vc, animated: true)
}
#objc func save() {
//save befor dismiss
dismiss(animated: true, completion: nil)
/*
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "Recipes"
guard let newEntity = NSEntityDescription.entity(forEntityName: entityName, in: context) else {
return
}
let newRecipe = NSManagedObject(entity: newEntity, insertInto: context)
let name = tf_one.text
let id = recipeId
let category = 1
let persons = 2
newRecipe.setValue(name, forKey: "name")
newRecipe.setValue(id, forKey: "id")
newRecipe.setValue(category, forKey: "category")
newRecipe.setValue(persons, forKey: "persons")
do {
try context.save()
print("Gespeichert!")
let vc: ViewController!
vc = ViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: false)
} catch {
print(error)
}*/
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(name_array.count)
return name_array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! IngredientCell
cell.nameLabel.text = name_array[indexPath.row]
cell.descriptionLabel.text = "text"
cell.personsLabel.text = "text"
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "/ENTRY/"
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
do {
let results = try context.fetch(request)
guard results.count > 0 else {
return
}
if let deleteEntity = results[indexPath.row] as? NSManagedObject {
context.delete(deleteEntity) //Das Entity ist hier nur markiert als gelöscht, für evt arbeiten am Item
if context.hasChanges {
do {
try context.save()
print("Datensatz gelöscht!")
let vc: ViewController!
vc = ViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: false)
} catch {
print(error)
}
}
}
} catch {
print(error)
}
}
}
}
import UIKit
import CoreData
class AddNewIngredientViewController: UIViewController {
let viewExample = UIView()
let titleLabel = UILabel()
let tf_one = UITextField()
let unitTicker = UISegmentedControl()
let unitLabel = UILabel()
let slider = UISlider()
let submitButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white.withAlphaComponent(0)
createViewControllerItems()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
func createViewControllerItems() {
// - MARK: View
viewExample.backgroundColor = .white
viewExample.layer.cornerRadius = 15
viewExample.clipsToBounds = true
view.addSubview(viewExample)
viewExample.translatesAutoresizingMaskIntoConstraints = false
viewExample.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -200).isActive = true
viewExample.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewExample.widthAnchor.constraint(equalToConstant: 330).isActive = true
viewExample.heightAnchor.constraint(equalToConstant: 350).isActive = true
// - MARK: Titel
titleLabel.text = "Zutat hinzufügen"
titleLabel.textColor = .black
titleLabel.textAlignment = .center
titleLabel.font = UIFont(name: "Chalkduster", size: 24)
view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 250).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Überschrift
tf_one.placeholder = "Name"
tf_one.borderStyle = .roundedRect
tf_one.keyboardType = .default
tf_one.spellCheckingType = .yes
tf_one.smartInsertDeleteType = .yes
tf_one.autocorrectionType = .yes
tf_one.autocapitalizationType = .sentences
view.addSubview(tf_one)
tf_one.translatesAutoresizingMaskIntoConstraints = false
tf_one.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
tf_one.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_one.widthAnchor.constraint(equalToConstant: 310).isActive = true
tf_one.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Unit Ticker
unitTicker.insertSegment(withTitle: "ml", at: 0, animated: true)
unitTicker.insertSegment(withTitle: "mg", at: 1, animated: true)
unitTicker.insertSegment(withTitle: "unit", at: 2, animated: true)
unitTicker.addTarget(self, action: #selector(updateLabel), for: UIControl.Event.allEvents)
view.addSubview(unitTicker)
unitTicker.translatesAutoresizingMaskIntoConstraints = false
unitTicker.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
unitTicker.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// - MARK: Slider
slider.maximumValue = 10
slider.addTarget(self, action: #selector(updateLabel), for: UIControl.Event.allEvents)
view.addSubview(slider)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.topAnchor.constraint(equalTo: view.topAnchor, constant: 200).isActive = true
slider.widthAnchor.constraint(equalToConstant: 310).isActive = true
slider.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// - MARK: Bestätigen
submitButton.layer.cornerRadius = 20
submitButton.clipsToBounds = true
submitButton.backgroundColor = .lightGray
submitButton.setTitle("ok", for: UIControl.State.normal)
submitButton.titleLabel?.font = UIFont(name: "Chalkduster", size: 24)
submitButton.addTarget(self, action: #selector(save), for: .touchUpInside)
view.addSubview(submitButton)
submitButton.translatesAutoresizingMaskIntoConstraints = false
submitButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 100).isActive = true
submitButton.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 50).isActive = true
submitButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
submitButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
#objc func updateLabel() {
unitLabel.textAlignment = .center
if unitTicker.selectedSegmentIndex == 0{
unitLabel.text = "\(Int(slider.value)) ml"
slider.maximumValue = 1000
}else if unitTicker.selectedSegmentIndex == 1{
unitLabel.text = "\(Int(slider.value)) mg"
slider.maximumValue = 1000
}else if unitTicker.selectedSegmentIndex == 2{
unitLabel.text = "\(Int(slider.value)) unit"
slider.maximumValue = 10
}
view.addSubview(unitLabel)
unitLabel.translatesAutoresizingMaskIntoConstraints = false
unitLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 230).isActive = true
unitLabel.widthAnchor.constraint(equalToConstant: 310).isActive = true
unitLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
#objc func save() {
let viewController = AddNewRecipeViewController()
viewController.name_array.append(tf_one.text!)
print(viewController.name_array)
dismiss(animated: true, completion: nil)
}
}
Thanks!
Tom
You can pass data between two viewController by using the delegate method or NSNotificaton. For more info visit about NSNotification: https://developer.apple.com/documentation/foundation/nsnotification
For delegate method you can check https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef

Swift bool data not passed to tableview cell

I have a tableview that gets the data from an API. There is a isUnlocked variable that is also getting value from the API.
But besides the title and picture, the isUnlocked variable still has the default value set in the custom tableview class.
I pass the value in cellForRow function like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if tableView.tag == 1 {
let c = tableView.dequeueReusableCell(withIdentifier: CourseViewIdentifiers.courses.rawValue, for: indexPath) as! CourseViewTableViewCell
c.isUnlocked = tableData[indexPath.row].unlocked
c.selectionStyle = .none
c.courseTitle.text = tableData[indexPath.row].title
c.courseImage.loadImageFrom(urlString: tableData[indexPath.row].pic)
cell = c
}
return cell
}
And this is the custom class for tableview cell:
public var isUnlocked: Bool = false
private lazy var backView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
view.layer.cornerRadius = 5
view.layer.borderColor = UIColor.lightGray.cgColor
view.layer.borderWidth = 0.5
return view
}()
public lazy var courseImage: WebImageloader = {
let imv = WebImageloader()
imv.contentMode = .scaleAspectFill
return imv
}()
public lazy var courseTitle: UILabel = {
let label = UILabel()
label.font = UIFont.iransans(size: 18)
label.textAlignment = .right
return label
}()
private lazy var courseButton1: UIButton = {
let button = UIButton(type: .system)
if !isUnlocked {
button.setBackgroundImage(#imageLiteral(resourceName: "locked"), for: .normal)
button.isEnabled = false
}else{
button.setBackgroundImage(#imageLiteral(resourceName: "play"), for: .normal)
}
return button
}()
private lazy var courseButton2: UIButton = {
let button = UIButton(type: .system)
button.setBackgroundImage(#imageLiteral(resourceName: "download"), for: .normal)
return button
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: CourseViewIdentifiers.courses.rawValue)
print(isUnlocked,"***************")
setupUI()
contentView.backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
private func setupUI() {
backView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(backView)
backView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15).isActive = true
backView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
backView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5).isActive = true
backView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5).isActive = true
backView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
courseImage.translatesAutoresizingMaskIntoConstraints = false
backView.addSubview(courseImage)
courseImage.rightAnchor.constraint(equalTo: backView.rightAnchor, constant: -10).isActive = true
courseImage.centerYAnchor.constraint(equalTo: backView.centerYAnchor).isActive = true
courseImage.heightAnchor.constraint(equalToConstant: 50).isActive = true
courseImage.widthAnchor.constraint(equalToConstant: 50).isActive = true
courseImage.layer.cornerRadius = 25
courseImage.clipsToBounds = true
courseButton1.translatesAutoresizingMaskIntoConstraints = false
backView.addSubview(courseButton1)
courseButton1.leftAnchor.constraint(equalTo: backView.leftAnchor, constant: 10).isActive = true
courseButton1.centerYAnchor.constraint(equalTo: backView.centerYAnchor).isActive = true
courseButton1.heightAnchor.constraint(equalToConstant: 30).isActive = true
courseButton1.widthAnchor.constraint(equalToConstant: 30).isActive = true
courseButton1.layer.cornerRadius = 15
courseButton1.clipsToBounds = true
if isUnlocked {
courseButton2.translatesAutoresizingMaskIntoConstraints = false
backView.addSubview(courseButton2)
courseButton2.leftAnchor.constraint(equalTo: courseButton1.rightAnchor, constant: 10).isActive = true
courseButton2.centerYAnchor.constraint(equalTo: backView.centerYAnchor).isActive = true
courseButton2.heightAnchor.constraint(equalToConstant: 30).isActive = true
courseButton2.widthAnchor.constraint(equalToConstant: 30).isActive = true
courseButton2.layer.cornerRadius = 15
courseButton2.clipsToBounds = true
}
courseTitle.translatesAutoresizingMaskIntoConstraints = false
backView.addSubview(courseTitle)
courseTitle.rightAnchor.constraint(equalTo: courseImage.leftAnchor, constant: -10).isActive = true
courseTitle.topAnchor.constraint(equalTo: courseImage.topAnchor).isActive = true
courseTitle.leftAnchor.constraint(equalTo: courseButton1.rightAnchor, constant: 10)
}
The isUnlocked variable has the default False value no matter what I set it in the cellForRow function. Guys it's killing me please help.
I think your value is being set, but your UI is not being updated because it only gets set during initialization, where obviously the value of the Bool will always be the default value (false). So instead of calling setUpUI() in init(), call it after dequeue-ing.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if tableView.tag == 1 {
let c = tableView.dequeueReusableCell(withIdentifier: CourseViewIdentifiers.courses.rawValue, for: indexPath) as! CourseViewTableViewCell
c.isUnlocked = tableData[indexPath.row].unlocked
c.selectionStyle = .none
c.courseTitle.text = tableData[indexPath.row].title
c.courseImage.loadImageFrom(urlString: tableData[indexPath.row].pic)
c.setUpUI()
//print(c.isUnlocked)
cell = c
}
return cell
}
Or atleast move the set up of courseButton2 to another method and call that method.
Problem solved by adding this block of code:
DispatchQueue.main.async {
if DownloadManager.isDownloading(url: self.course_url!) {
self.courseButton2.alpha = 0.5
self.courseButton2.isEnabled = false
}
}
Just add this code to the cell, and your ok to go!

Swift 4: Two UITableView in one UIViewController, scroll function not working

I have two UITableView in one UIViewController and feeding those tables with data from Rest API. it all seems works fine but then I realize that the scrolling is not working. I have other UITableViewController with scrolling working just fine.
here is the code of my ViewController
here is the class
class DashboardController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var lowerTableView: UITableView = {
let tv = UITableView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.isScrollEnabled = true
tv.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
return tv
}()
var upperTableView: UITableView = {
let tv = UITableView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.isScrollEnabled = true
tv.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tv
}()
...
viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
if user?.status != "success" {
handleLogout()
} else {
self.navigationItem.title = "Dashboard"
self.userName.text = user?.fullname
upperTableView.dataSource = self
upperTableView.delegate = self
lowerTableView.dataSource = self
lowerTableView.delegate = self
handleRefresh()
upperTableView.register(QuizCell.self, forCellReuseIdentifier: "cell")
lowerTableView.register(QuizCell.self, forCellReuseIdentifier: "cell1")
setupNavBarButtons()
setupDashboard()
}
}
...
and numberOfRowsInSection method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count:Int?
if tableView == self.upperTableView {
count = sampleData.count
// count = 10
}
if tableView == self.lowerTableView {
count = closedQuizList.count
// count = 10
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.upperTableView {
cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let datum = sampleData[indexPath.row]
cell?.textLabel?.text = datum.name
cell?.detailTextLabel?.text = datum.quiz_type+" - "+datum.start_date!+" - "+datum.end_date!
cell?.detailTextLabel?.textColor = .blue
}
if tableView == self.lowerTableView {
cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
let datum = closedQuizList[indexPath.row]
cell?.textLabel?.text = datum.name + " (" + datum.score! + ")"
}
return cell!
}
here is setupDashboard() code
func setupDashboard() {
let upperDashboard = UIView()
upperDashboard.backgroundColor = .white
let lowerDashboard = UIView()
lowerDashboard.backgroundColor = .white
let dashboardStackView = UIStackView(arrangedSubviews: [upperDashboard, lowerDashboard])
dashboardStackView.translatesAutoresizingMaskIntoConstraints = false
dashboardStackView.distribution = .fillEqually
dashboardStackView.axis = .vertical
view.addSubview(dashboardStackView)
NSLayoutConstraint.activate([
dashboardStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
dashboardStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
dashboardStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
dashboardStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
// upperDashboard part
upperDashboard.addSubview(userName)
upperDashboard.addSubview(userStatus)
upperDashboard.addSubview(upperLabel)
upperDashboard.addSubview(upperLabelSeparator)
upperDashboard.addSubview(statsButton)
upperDashboard.addSubview(upperTableView)
NSLayoutConstraint.activate([
// userName
userName.topAnchor.constraint(equalTo: upperDashboard.topAnchor, constant: 10),
userName.leftAnchor.constraint(equalTo: upperDashboard.leftAnchor, constant: 12),
userName.widthAnchor.constraint(equalTo: upperDashboard.widthAnchor, multiplier: 0.5),
// userStatus
userStatus.topAnchor.constraint(equalTo: upperDashboard.topAnchor, constant: 10),
userStatus.rightAnchor.constraint(equalTo: upperDashboard.rightAnchor, constant: -12),
userStatus.widthAnchor.constraint(equalTo: upperDashboard.widthAnchor, multiplier: 0.5),
// upperLabel
statsButton.topAnchor.constraint(equalTo: userName.bottomAnchor, constant: 6),
statsButton.leftAnchor.constraint(equalTo: upperDashboard.leftAnchor, constant: 12),
statsButton.widthAnchor.constraint(equalTo: upperDashboard.widthAnchor, constant: -24),
statsButton.heightAnchor.constraint(equalToConstant: 40),
// statsButton
upperLabel.topAnchor.constraint(equalTo: statsButton.bottomAnchor, constant: 6),
upperLabel.leadingAnchor.constraint(equalTo: statsButton.leadingAnchor),
upperLabel.widthAnchor.constraint(equalTo: statsButton.widthAnchor),
// upperLabelSeparator
upperLabelSeparator.topAnchor.constraint(equalTo: upperLabel.bottomAnchor),
upperLabelSeparator.leadingAnchor.constraint(equalTo: upperLabel.leadingAnchor),
upperLabelSeparator.widthAnchor.constraint(equalTo: upperLabel.widthAnchor),
upperLabelSeparator.heightAnchor.constraint(equalToConstant: 4),
// upperTableView
upperTableView.topAnchor.constraint(equalTo: upperLabelSeparator.bottomAnchor),
upperTableView.trailingAnchor.constraint(equalTo: lowerDashboard.trailingAnchor, constant: -12),
upperTableView.leadingAnchor.constraint(equalTo: lowerDashboard.leadingAnchor, constant: 12),
upperTableView.bottomAnchor.constraint(equalTo: lowerDashboard.bottomAnchor)
])
// lowerDashboard part
lowerDashboard.addSubview(lowerLabel)
lowerDashboard.addSubview(lowerLabelSeparator)
lowerDashboard.addSubview(lowerTableView)
NSLayoutConstraint.activate([
// lowerLabel
lowerLabel.topAnchor.constraint(equalTo: lowerDashboard.topAnchor),
lowerLabel.leftAnchor.constraint(equalTo: lowerDashboard.leftAnchor, constant: 12),
lowerLabel.widthAnchor.constraint(equalTo: lowerDashboard.widthAnchor, constant: -24),
// lowerLabelSeparator
lowerLabelSeparator.topAnchor.constraint(equalTo: lowerLabel.bottomAnchor),
lowerLabelSeparator.leadingAnchor.constraint(equalTo: lowerLabel.leadingAnchor),
lowerLabelSeparator.widthAnchor.constraint(equalTo: lowerLabel.widthAnchor),
lowerLabelSeparator.heightAnchor.constraint(equalToConstant: 4),
// lowerTableView
lowerTableView.topAnchor.constraint(equalTo: lowerLabelSeparator.bottomAnchor),
lowerTableView.trailingAnchor.constraint(equalTo: lowerDashboard.trailingAnchor, constant: -12),
lowerTableView.leadingAnchor.constraint(equalTo: lowerDashboard.leadingAnchor, constant: 12),
lowerTableView.bottomAnchor.constraint(equalTo: lowerDashboard.bottomAnchor)
])
}
class DashboardController: UIViewController, UITableViewDataSource, UITableViewDelegate {
lazy var lowerTableView: UITableView = {
let tv = UITableView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.isScrollEnabled = true
tv.isUserInteractionEnabled = true
tv.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
return tv
}()
lazy var upperTableView: UITableView = {
let tv = UITableView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.isScrollEnabled = true
tv.isUserInteractionEnabled = true
tv.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tv
}()
This way you don't have to set the delegate and dataSource inside of viewDidLoad.
I also would not call tableview.register again inside viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
if user?.status != "success" {
handleLogout()
} else {
self.navigationItem.title = "Dashboard"
self.userName.text = user?.fullname
self.view.addSubView(lowerTableView)
self.view.addSUbView(upperTableView)
handleRefresh()
setupNavBarButtons()
setupDashboard()
}
}
If this doesn't work can you show me what setupDashboard() does?

Resources