Swift UITableViewCell Border becomes angular when deleted - swift

I just created a tableView and I want to implement a function which allows the user to delete a row. For this I have implemented this function:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Zeile löschen (Z.b. dann aus dem Array oder aus dem dauerhaftem CoreData)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
But you still have to know for my question that I configured my cell with this code:
let cell = tableView.dequeueReusableCell(withIdentifier: "timerCell", for: indexPath) as! timerTableViewCell
cell.layer.borderWidth = 1
// cell.layer.borderColor = CGColor(red: 41 / 255, green: 171 / 255, blue: 226 / 255, alpha: 1)
cell.layer.borderColor = CGColor(red: 0, green: 0, blue: 0, alpha: 1)
cell.textLabel?.text = "Timer"
cell.detailTextLabel!.text = "12:40"
cell.layer.cornerRadius = 18
cell.clipsToBounds = true
cell.layoutMargins.left = 30
cell.layoutMargins.right = 30
return cell
But now I have a problem with deleting the cells and the frame of my cell: My cell has a round border and if I now want to delete this cell in my app, the border suddenly becomes square and does not become again round even after the deletion is canceled. Unfortunately that looks very bad. Here are the screenshots:
1.
2.
3.
Has anyone ever had this problem or ideas and could help me?
PS: I would like it to look like this:

That would imply that the cell's CALayer is being reconstructed (probably when the cell is resized to show the delete button).
You might want to create a custom view cell class and override the func layoutSublayers(of: CALayer) of the cell (which it inherits though UIView and CALayerDelegate). Inside of that method you can call super then set the corner radius on your view's layer.

I have now tried something new and simply put a view on my cell and then round out this view instead of the cell directly. But I always get an error message with the required init. This is my code:
let view: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.borderWidth = 1.5
view.layer.borderColor = CGColor(red: 0, green: 0, blue: 0, alpha: 1)
view.layer.cornerRadius = 18
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "timerCell")
super.awakeFromNib()
setConstrains()
}
func setConstrains() {
self.addSubview(view)
view.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
view.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
view.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
view.heightAnchor.constraint(equalTo: self.heightAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}```

Related

Dinamically fill UITableViewCell with buttons side by side

Continuing to resolve the task of UITableViewCell, I could fix all UIButtons side by side but they are not appearing properly in executing time. Sometimes the second row mix with the third.
I tryied with cell.layoutIfNeeded() but still wrong.
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DetailViewCell
let array = sections[indexPath.section].filters
var hStackView = UIStackView()
var vStackView = UIStackView()
vStackView.axis = .vertical
vStackView.distribution = .fillEqually
vStackView.spacing = 12
vStackView.alignment = .top
for (index, item) in array.enumerated() {
let btn = UIButton()
btn.setTitle(item.title, for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 12)
btn.setTitleColor(.black, for: .normal)
btn.backgroundColor = UIColor(red: 220, green: 220, blue: 220)
btn.layer.cornerRadius = 15;
btn.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
btn.tag = index
if index % 2 == 0 {
hStackView = UIStackView()
hStackView.axis = .horizontal
hStackView.spacing = 12
hStackView.alignment = .fill
hStackView.distribution = .fillEqually
hStackView.addArrangedSubview(btn)
if (index + 1) == array.count {
vStackView.addArrangedSubview(hStackView)
hStackView.leadingAnchor.constraint(equalTo: vStackView.leadingAnchor).isActive = true
hStackView.widthAnchor.constraint(equalTo: vStackView.widthAnchor, multiplier: 0.5).isActive = true
}
}
else {
hStackView.addArrangedSubview(btn)
vStackView.addArrangedSubview(hStackView)
hStackView.leadingAnchor.constraint(equalTo: vStackView.leadingAnchor).isActive = true
hStackView.trailingAnchor.constraint(equalTo: vStackView.trailingAnchor).isActive = true
}
}
cell.addSubview(vStackView)
vStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cell.leadingAnchor.constraint(equalTo: vStackView.leadingAnchor, constant: -8),
cell.trailingAnchor.constraint(equalTo: vStackView.trailingAnchor, constant: 8),
cell.topAnchor.constraint(equalTo: vStackView.topAnchor, constant: -8),
cell.bottomAnchor.constraint(equalTo: vStackView.bottomAnchor, constant: 8)
])
cell.layoutIfNeeded()
return cell
You should look up information about dequeueReusableCell
It works somehow like this: when you get a cell in tableView(_: cellForRowAt:) from dequeueReusableCell you are provided with a ready-made cell that was used somewhere. It follows from this that all content that the cells differ in must be placed in the cell only in this method
Note, the button with a label сссс is clearly from the top cell
I would advise you to declare in your UITableViewCell subclass a method in which you put content inside and call it every time inside tableView(_:cellForRowAt:)
You can use a storyboard if you like, but if you want to do it with code, I can suggest custom UITableViewCell subclass template:
class CustomTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// MARK: - Public methods
func setContent(_ content: <# ContentType #>) {
}
// MARK: - Private setup methods
private func setupView() {
makeConstraints()
}
private func makeConstraints() {
NSLayoutConstraint.activate([
])
}
}

Tableview y origin not animating properly when navigationItem.titleView is hidden (Swift 5)

I’m trying to get the tableView to move up when the search bar does. Take a look at the problem:
I think I see what the issue is here, but I can't think of a solution. In SearchResultsUpdating I have an animation block:
func updateSearchResults(for searchController: UISearchController) {
UIView.animateKeyframes(withDuration: 1, delay: 0, options: UIView.KeyframeAnimationOptions(rawValue: 7)) {
self.tableView.frame = CGRect(x: 20, y: self.view.safeAreaInsets.top, width:
self.view.frame.size.width-40, height: self.view.frame.size.height -
self.view.safeAreaInsets.top)
}
}
It seems to me that the animation block is only receiving the previous coordinates for the y origin, hence it is animating out of sync. I tried adding a target to the tableView, or navigationBar, or the searchBarTextField instead, but nothing worked.
Any help is appreciated, thanks!
EDIT: After implementing Shawn's second suggestion this was the result:
I can't imagine why it isn't animating smoothly now... very frustrating!
EDIT 2 - Requested Code:
class ViewController: UIViewController{
//City TableView
let cityTableView = UITableView()
let searchVC: UISearchController = {
let searchController = UISearchController(searchResultsController: nil)
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "Search"
return searchController
}()
//viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
//Do any setup for the view controller here
setupViews()
//CityViewController
setupCityViewTableView()
}
//setupViews
func setupViews(){
//NAVIGATIONBAR:
//title
title = "Weather"
//set to hidden because on initial load there is a scroll view layered over top of the CityViewTableView (code not shown here). This gets set to false when the scrollView alpha is set to 0 and the CityViewTableView is revealed
navigationController?.navigationBar.isHidden = true
navigationController?.navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
//NAVIGATION ITEM:
navigationItem.searchController = searchVC
//UISEARCHBARCONTROLLER:
searchVC.searchResultsUpdater = self
}
}
//MARK: -CityViewController Functions
extension ViewController{
//setUp TableView
func setupCityViewTableView(){
cityTableView.translatesAutoresizingMaskIntoConstraints = false
//set tableView delegate and dataSource
cityTableView.delegate = self
cityTableView.dataSource = self
//background color
cityTableView.backgroundColor = .black
//separator color
cityTableView.separatorColor = .clear
//is transparent on initial load
cityTableView.alpha = 0
//set tag
cityTableView.tag = 1000
//hide scroll indicator
cityTableView.showsVerticalScrollIndicator = false
//register generic cell
cityTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cityCell")
//add subview
view.addSubview(cityTableView)
//Auto Layout
cityTableView.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: 20).isActive = true
cityTableView.topAnchor
.constraint(equalTo: view.topAnchor,
constant: 0).isActive = true
cityTableView.trailingAnchor
.constraint(equalTo: view.trailingAnchor,
constant: -20).isActive = true
cityTableView.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: 0).isActive = true
}
}
//MARK: -TableView Controller
extension ViewController: UITableViewDelegate,
UITableViewDataSource{
//number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
if tableView.tag == 1000{
return 5
}
return self.models[tableView.tag].count
}
//cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
//CityViewController
if tableView.tag == 1000{
let cell = tableView.dequeueReusableCell(withIdentifier:
"cityCell", for: indexPath)
cell.textLabel?.text = "Test"
cell.textLabel?.textAlignment = .center
cell.backgroundColor = .systemGray
cell.selectionStyle = .none
cell.layer.cornerRadius = 30
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 5
cell.layer.cornerCurve = .continuous
return cell
}
//WeatherViewController
//code here for scrollView tableViews
}
//Height for row
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if tableView.tag == 1000{
return view.frame.size.height/7
}
return view.frame.size.height/10
}
//Should Highlight Row
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
if tableView.tag == 1000{
return true
}
return false
}
//Did select row
func tableView(_ tableView: UITableView, didSelectRowAt
indexPath: IndexPath) {
//calls function for segue to Weather Scroll View (not shown)
if tableView.tag == 1000{
segueToWeatherView(indexPath: indexPath)
}
}
}
EDIT 3: When I comment out another function it finally works, but I'm not sure exactly why, or how to fix it. This is the function in question, addSubViews()
//setup viewController
func addSubViews(){
//add weatherView as subView of ViewController
view.addSubview(weatherView)
//add subviews to weatherView
weatherView.addSubview(scrollView)
weatherView.addSubview(pageControl)
weatherView.addSubview(segueToCityViewButton)
weatherView.addSubview(segueToMapViewButton)
}
Specifically, it works when I comment out this line:
view.addSubview(weatherView)
Here is all the code concerning the setting up of the weatherView and all of its subViews:
//Any additional setup goes here
private func setupViews(){
//VIEWCONTROLLER:
//title
title = "Weather"
//Background color of view Controller
view.backgroundColor = .darkGray
//WEATHERVIEW:
//Background color of weather view Controller
weatherView.backgroundColor = .clear
//weatherView frame
weatherView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)
//SCROLLVIEW:
//background color of scroll view
scrollView.backgroundColor = .clear
//scrollView frame
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)
//changed
//PAGECONTROL:
//page control frame
pageControl.frame = CGRect(x: 0, y: view.frame.height-view.frame.size.height/14, width: view.frame.width, height: view.frame.size.height/14)
//TRANSITIONVIEW:
//TransitionView frame
transitionView.frame = CGRect(x: 20, y: 0, width: view.frame.size.width-40, height: view.frame.size.height)
//BUTTONS:
//segue to CityView
segueToCityViewButton.frame = CGRect(x: (weatherView.frame.width/5*4)-20, y: weatherView.frame.height-weatherView.frame.size.height/14, width: weatherView.frame.width/5, height: pageControl.frame.height)
//segue to MapView:
segueToMapViewButton.frame = CGRect(x: 20, y: weatherView.frame.height-weatherView.frame.size.height/14, width: weatherView.frame.width/5, height: pageControl.frame.height)
//LABELS:
transitionViewLabel.frame = transitionView.bounds
//NAVIGATIONBAR:
//set to hidden on initial load
navigationController?.navigationBar.isHidden = true
navigationController?.navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
//NAVIGATION ITEM:
navigationItem.searchController = searchVC
//UISEARCHBARCONTROLLER:
searchVC.searchResultsUpdater = self
}
For the sake of being thorough, here is the full viewDidLoad() Function:
override func viewDidLoad() {
super.viewDidLoad()
//MARK: View Controller
//These two will eventually be moved to the DispatchQueue in APICalls.swift
configureScrollView()
pageControl.numberOfPages = models.count
//Do any setup for the view controller here
setupViews()
//setup ViewController
addSubViews()
//Add Target for the pageControl
addTargetForPageControl()
//MARK: CityViewController
setupCityViewTableViews()
}
EDIT 4: With the following changes in viewDidLoad(), I finally got it to work!
override func viewDidLoad() {
super.viewDidLoad()
//MARK: CityViewController
//Moved to a position before setting up the other views
setupCityViewTableViews()
//MARK: View Controller
//These two will eventually be moved to the DispatchQueue in APICalls.swift
configureScrollView()
pageControl.numberOfPages = models.count
//Do any setup for the view controller here
setupViews()
//setup ViewController
addSubViews()
//Add Target for the pageControl
addTargetForPageControl()
}
Doing it the way you are doing it right now is a way to do it but I think it is the most challenging way to do it for several reasons:
You don't have much control and access to the implementation of the search controller animation within the navigation bar so getting the right coordinates might be a task
Even if you did manage to get the right coordinates, trying to synchronize your animation frames and timing to look in sync and seamless with the search animation on the nav bar will be tricky
I suggest the 2 following alternatives to what you are currently doing where you will get the news experience pretty much for free out of the box.
Option 1: Use a UITableViewController instead of a UIViewController
This is all the code using a UITableViewController and adding a UISearchController to the navigation bar.
class NewsTableViewVC: UITableViewController
{
private let searchController: UISearchController = {
let sc = UISearchController(searchResultsController: nil)
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = "Search"
sc.searchBar.autocapitalizationType = .allCharacters
return sc
}()
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .black
title = "Weather"
// Ignore this as you have you own custom cell class
tableView.register(CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier)
setUpNavigationBar()
}
private func setUpNavigationBar()
{
navigationItem.searchController = searchController
}
}
This is the experience you can expect
Option 2: Use auto layouts rather than frames to configure your UITableView
If you don't want to use a UITableViewController, configure your UITableView using auto layout rather than frames which has a little more work but not too much:
class NewsTableViewVC: UIViewController, UITableViewDataSource, UITableViewDelegate
{
private let searchController: UISearchController = {
let sc = UISearchController(searchResultsController: nil)
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = "Search"
sc.searchBar.autocapitalizationType = .allCharacters
return sc
}()
private let tableView = UITableView()
override func viewDidLoad()
{
super.viewDidLoad()
// Just to show it's different from the first
view.backgroundColor = .purple
title = "Weather"
setUpNavigationBar()
setUpTableView()
}
private func setUpNavigationBar()
{
navigationItem.searchController = searchController
}
private func setUpTableView()
{
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier)
tableView.dataSource = self
tableView.delegate = self
tableView.backgroundColor = .clear
view.addSubview(tableView)
// Auto Layout
tableView.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: 0).isActive = true
// This important, configure it to the top of the view
// NOT the safe area margins to get the desired result
tableView.topAnchor
.constraint(equalTo: view.topAnchor,
constant: 0).isActive = true
tableView.trailingAnchor
.constraint(equalTo: view.trailingAnchor,
constant: 0).isActive = true
tableView.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: 0).isActive = true
}
}
You can expect the following experience:
Update
This is based on your updated code, you missed one small detail which might be impacting the results you see and this is the top constraint of the UITableView.
You added the constraint to the safeAreaLayoutGuide top anchor:
cityTableView.topAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: 0).isActive = true
My recommendation from the code above if you notice is to add it to the view top constraint
// This important, configure it to the top of the view
// NOT the safe area margins to get the desired result
cityTableView.topAnchor
.constraint(equalTo: view.topAnchor,
constant: 0).isActive = true
Give this a go and see if you come close to getting what you expect ?
Here is a link to the complete code of my implementation if it helps:

How to create dynamic self-sizing height UITableViewCell in swift?

Hey I have a tableview controller and I want to create dynamic cell height with label and ImageViews. I tried something like below but it doesn't work for me. Where is the mistake about code. Any idea ?
class NotificationsPageController: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(NotificationCell.self, forCellReuseIdentifier: notCell)
view.backgroundColor = .black
tableView.estimatedRowHeight = 60
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
class NotificationCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 18/255, green: 18/255, blue: 18/255, alpha: 1)
setNotificationAnchors()
}
fileprivate func setNotificationAnchors() {
addSubview(notiType)
notiType.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 6, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
//notiType.centerYAnchor.constraint(equalTo: notiImage.centerYAnchor).isActive = true
addSubview(notiImage)
notiImage.anchor(top: nil, left: nil, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 0, height: 0)
notiImage.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
First, I strongly recommend you learn how constraints and auto-layout work. The fact that the code you posted shows you are using a .anchor(top:left:bottom:right:...) type of "constraint helper" indicates that you don't understand it yet.
So, here is a simple example. Note that this is not a complete implementation, but it should get you headed in the right direction:
class NotificationCell: UITableViewCell {
let notiType: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
return v
}()
let notiImage: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 18/255, green: 18/255, blue: 18/255, alpha: 1)
setNotificationAnchors()
}
fileprivate func setNotificationAnchors() {
contentView.addSubview(notiType)
contentView.addSubview(notiImage)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain label Top, Leading, Bottom to contentView margins
notiType.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
notiType.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
notiType.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// constrain image view Right and centerY to contentView margins
notiImage.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
notiImage.centerYAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
// guessing square (1:1 ratio) image view, 24 x 24
notiImage.widthAnchor.constraint(equalToConstant: 24.0),
notiImage.heightAnchor.constraint(equalTo: notiImage.widthAnchor),
// leave 8-pts space between label and image view
notiType.trailingAnchor.constraint(equalTo: notiImage.leadingAnchor, constant: -8.0),
])
// during development, so we can easily see the element frames
notiType.backgroundColor = .cyan
notiImage.backgroundColor = .red
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ExampleTableViewController: UITableViewController {
let notCell = "notCell"
let theData: [String] = [
"Single line label.",
"This lable will have enough text that it will need to wrap onto multiple lines.",
"Another single line cell.",
"Here is a description of a table view cell: Defines the attributes and behavior of cells in a table view. You can set a table cell's selected-state appearance, support editing functionality, display accessory views (such as a switch control), and specify background appearance and content indentation.",
"This is the fifth row.",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(NotificationCell.self, forCellReuseIdentifier: notCell)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: notCell, for: indexPath) as! NotificationCell
cell.notiType.text = theData[indexPath.row]
// set image here...
//cell.notiImage.image = ...
return cell
}
}
Result - Multi-line label (cyan background) and 24x24 image (red background):
By the way... Step-by-step tutorials on this can be found in many, many places - a little tough to think you searched and couldn't find anything like this.
Follow the steps below:
Add the below lines in ViewDidLoad
tableView.dataSource = self
tableView.delegate = self
// Set automatic dimensions for row height
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = UITableViewAutomaticDimension
In XIB or Cell if you have label update the numberOLines = 0 and make the lineBreakMode = truncateTail
Update constraints to top, bottom, right and left to its superView

Swift: tableview cell change width and postion

I create chatroom
I want user 1 chats position in right and user 2 chats position in left like below image
My code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyChatCell
var frame = cell.frame
let newWidth = frame.width * 0.50
let space = (frame.width - newWidth) / 2
frame.size.width = newWidth
frame.origin.x += space
cell.frame = frame
return cell
}
But my code not work
Please consider using Anchor for your MyChatCellLeft and MyChatCellRight and anchor each example:
cell.messageLabel = "My Message"
cell.floatPosition = true // true left / false right
I must mention I haven't tested this. but you can try it and take it as a blueprint for what you are doing.. you also need to add some kind of logic of who is who... example user1 left user2 right. that depends on your logic...
class ChatBubbleCell: UITableViewCell{
var position: Bool = false
let messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var floatPosition = Bool() {
didSet {
if(floatPosition == true){
position = true
} else {
position = false
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews(){
addSubview(messageLabel)
// lef position
if position == true {
let constrains = [messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 15),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
messageLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 20)
]
NSLayoutConstraint.activate(constrains)
} else {
let constrains = [messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 15),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
messageLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -30)
]
NSLayoutConstraint.activate(constrains)
}
}
}
Consider the cell to be the whole row and use a subview to update frames.
Update the frames on the method of will display cell for row at index path

Changing items within custom UITableViewCell Class

I have 2 classes - a UITableViewController and a custom UITableViewCell. I want to change the cell height for my UITableViewController, so I implement the following:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.frame.height*(1/12)
}
and it works! The cell height changes. Now, I go into my custom UITableViewCell class
import UIKit
class TableViewCell: UITableViewCell {
var time:UILabel = UILabel()
var name:UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) // the common code is executed in this super call
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.frame.width*0.22, height: self.frame.height))
imageView.image = UIImage(named: "Person")
imageView.contentMode = .scaleAspectFit
imageView.isUserInteractionEnabled = true
self.addSubview(imageView)
let tap = UITapGestureRecognizer(target: self, action: #selector(signIn))
imageView.addGestureRecognizer(tap)
name = UILabel(frame: CGRect(x: self.frame.width*0.22, y: 0, width: self.frame.width*0.78, height: self.frame.height*0.7))
name.textColor = UIColor.gray
name.font = UIFont(name: name.font!.fontName, size: 30)
name.adjustsFontSizeToFitWidth = true
self.addSubview(name)
time = UILabel(frame: CGRect(x: self.frame.width*0.22, y: self.frame.height*0.65, width: self.frame.width*0.78, height: self.frame.height*0.3))
time.textColor = UIColor.gray
time.font = UIFont(name: time.font!.fontName, size: 15)
time.adjustsFontSizeToFitWidth = true
self.addSubview(time)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#objc func signIn(tap: UITapGestureRecognizer) {
let tappedImage = tap.view as! UIImageView
tappedImage.image = UIImage(named: "PersonClocked")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
So, I now edit the function inside my controller to call the cells.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.layer.borderColor = UIColor.red.cgColor
cell.layer.borderWidth = 1
cell.name.text = names[indexPath.row]
cell.time.text = times[indexPath.row]
return cell
}
I expect it to all work now! But, it doesn't. For whatever reason, the methods get called in this order...
tableview(cellForRowAt) -> TableViewCell(override init) -> tableView(heightForRowAt)'
So, when I go to run it, it looks something like below. The cell is created with Swift's/Apple's default runtime tableView, then the cell size is changed, but everything inside the cell is still the size of the original default value. I need it to be the size of the cell. Any ideas?
Note - I added a border so you could see the size of the cell compared to the items.
Frame layout can't help in creating dynamic height tables you have to use dynamic tableViewCells and create the cell's contentView with constraints to get the height change with the current content
//
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) // the common code is executed in this super call
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.frame.width*0.22, height: self.frame.height))
imageView.image = UIImage(named: "Person")
imageView.contentMode = .scaleAspectFit
imageView.isUserInteractionEnabled = true
self.addSubview(imageView)
let tap = UITapGestureRecognizer(target: self, action: #selector(signIn))
imageView.addGestureRecognizer(tap)
name = UILabel(frame: CGRect(x: self.frame.width*0.22, y: 0, width: self.frame.width*0.78, height: self.frame.height*0.7))
name.textColor = UIColor.gray
name.font = UIFont(name: name.font!.fontName, size: 30)
name.adjustsFontSizeToFitWidth = true
self.addSubview(name)
time = UILabel(frame: CGRect(x: self.frame.width*0.22, y: self.frame.height*0.65, width: self.frame.width*0.78, height: self.frame.height*0.3))
time.textColor = UIColor.gray
time.font = UIFont(name: time.font!.fontName, size: 15)
time.adjustsFontSizeToFitWidth = true
self.addSubview(time)
imageView.translatesAutoresizingMaskIntoConstraints = false
name.translatesAutoresizingMaskIntoConstraints = false
time.translatesAutoresizingMaskIntoConstraints = false
imageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor,constant: 0).isActive = true
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 20).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 30).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 30).isActive = true
name.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 20).isActive = true
name.leadingAnchor.constraint(equalTo: imageView.leadingAnchor,constant: 20).isActive = true
name.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: -20).isActive = true
time.topAnchor.constraint(equalTo: name.topAnchor,constant: 20).isActive = true
time.leadingAnchor.constraint(equalTo: imageView.leadingAnchor,constant: 20).isActive = true
time.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: -20).isActive = true
time.bottomAnchor.constraint(equalTo: contentView.bottomAnchor,constant: -20).isActive = true
}
//
put this in viewDidLoad
tableview.estimatedRowHeight = 100
tableview.rowHeight = UITableViewAutomaticDimension
and don't implement heightForRowAt method