UITableViewController AutoLayoutConstraints - ios

I have drawn a border for the UITableViewCell
I have to set the leading and trailing constraints for the UITableViewCell, so that border will look correct.
To draw the border i have used the below code at cellForRowAt(:)
let today = tableView.dequeueReusableCell(withIdentifier: "todo", for: indexPath) as! DisplayTODOTaskCell
today.layer.masksToBounds = true
today.layer.cornerRadius = 0
today.layer.borderWidth = 1
today.layer.shadowOffset = CGSize(width: -1, height: 1)
let borderColor: UIColor = .black
today.layer.borderColor = borderColor.cgColor
When I used the below code , I have got the runtime error.
today.constraints(NSLayoutConstraint(item: today, attribute: .alignAllLeading, relatedBy: nil, toItem: self, attribute: self, multiplier: 1, constant: 10))
enter image description here
When I view from the phone left and right border are not visible.
I have used TableViewController.
I could draw the border for the cell by leaving space .
Need I achieve for subsequent cell in different section ,for that I need to know the height of the previous section.Please let me know how to achieve this
I have added below code in the UITableViewCell class.
override func layoutSubviews() {
self.frame = CGRect(x: 10.0, y: 5.0, width: 390.0, height: self.frame.size.height)
print("1111 Height = (self.frame.size.height)")
super.layoutSubviews()
}

Instead of customising Cell directly, Create UIView inside UITableViewCell.
Add UIView inside DisplayTODOTaskCellby using Storyboard. Give constraints, {top 2, left 5, bottom 2, right 5}.
Inside this UIView, you can design whatever you need.
Give, tableview and cell backgroundcolor as white.
let today = tableView.dequeueReusableCell(withIdentifier: "todo", for: indexPath) as! DisplayTODOTaskCell
today.createdView.layer.masksToBounds = true
today.createdView.layer.cornerRadius = 0
today.createdView.layer.borderWidth = 1
today.createdView.layer.shadowOffset = CGSize(width: -1, height: 1)
let borderColor: UIColor = .black
today.createdView.layer.borderColor = borderColor.cgColor

Related

ScrollView Doesn't Scroll in collectionViewCell

I am creating a collage app. The functionality I am looking at in my collectionViewCell is exactly like this: How to make a UIImage Scrollable inside a UIScrollView?
What I have done till now:
Created a custom collectionViewCell embedded a UIScrollView in it and an imageView inside the scrollView. I have set their constraints. Following is my code for collectionViewCell :
override init(frame: CGRect) {
super.init(frame: frame)
// addSubview(scrollView)
insertSubview(scrollView, at: 0)
contentView.isUserInteractionEnabled = true
scrollView.contentMode = .scaleAspectFill
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(imageView)
scrollContentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.isScrollEnabled = true
imageView.isUserInteractionEnabled = false
scrollView.delegate = self
scrollView.maximumZoomScale = 5.0
scrollView.backgroundColor = .red
scrollViewDidScroll(scrollView)
// scrollViewWillBeginDragging(scrollView)
let constraints = [
// Scroll View Constraints
scrollView.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 0.0)
scrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor,constant: 0.0),
scrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: 0.0),
scrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 0.0),
// ImageView Constraints
imageView.topAnchor.constraint(equalTo: self.scrollView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor),
imageView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor),
imageView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor)
]
NSLayoutConstraint.activate(constraints)
}
Another issue I face here is if I do insertSubview() instead of addSubview my collectionView function for didselectItemAt works fine else if I use addSubView the didselectItemFunction doesn't execute. Code For didSelectItem :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if selectedImage[indexPath.row] != UIImage(named: "empty-image") {
collectionViewCellClass.scrollView.isUserInteractionEnabled = true
collectionViewCellClass.scrollViewDidScroll(collectionViewCellClass.scrollView)
collectionViewCellClass.scrollView.showsHorizontalScrollIndicator = true
}else {
collectionViewCellClass.imageView.isUserInteractionEnabled = false
self.currentIndex = indexPath.row
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
imagePicker.delegate = self
imagePicker.allowsEditing = false
imagePicker.sourceType = .savedPhotosAlbum
present(imagePicker, animated: true, completion: nil)
}
}
}
Code for CellForItemAt IndexPath :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.image = selectedImage[indexPath.item]
// TODO: Round corners of the cell
cell.scrollView.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: cell.frame.size.height)
// cell.scrollView.contentSize = CGSize(width: cell.imageView.image!.size.width * 2, height: cell.imageView.image!.size.height * 2)
cell.scrollView.contentSize = cell.imageView.image?.size ?? .zero
cell.scrollView.contentMode = .center
cell.scrollView.isScrollEnabled = true
// cell.scrollContentView.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: cell.frame.size.height)
cell.imageView.frame = CGRect(x: 0, y: 0, width: cell.imageView.image!.size.width, height: cell.imageView.image!.size.height)
// cell.imageView.clipsToBounds = true
cell.backgroundColor = .black
return cell
}
I have tried changing the size of ImageView = cell.frame.size still no luck and changed the size for scrollView Frame as well to imageView.image.size but for some reason scrollView doesn't scroll.
Insertview vs addSubview
addSubview: add the view at the frontmost.
insertSubview: add the view at a specific index.
when you add your scrollview it using addSubView method it will block all the user interaction for the view's that are below it. That's why didSelect event is not triggered.
see this question
To make the scrollview scroll you need to set its contentSize which should be greater than scrollview's frame. try adding an image whose size is greater than collectionview cell.
scrollView.contentSize = imageView.bounds.size
Try adding the scrollView to the cells contentView:
self.contentView.addSubview(scrollView)
I finally found the Solution, following is what worked for me :
if selectedImage[indexPath.row] != UIImage(named: "empty-image"){
cell.contentView.isUserInteractionEnabled = true
}else{
cell.contentView.isUserInteractionEnabled = false
}
The UIImage(named: "empty-image") is the image that appears when a user doesn't have made any selection for his image. I wrote the above code in the cellForItemAt function. Also, I had to set imageView.frame equal to the original width and height of the image.
cell.imageView.frame = CGRect(x: 0, y: 0, width: cell.imageView.image!.size.width, height: cell.imageView.image!.size.height)
The above solution solved my problem. Hopefully, it will help someone.

Prevent UICollectionViewCell drop shadow from casting onto other cells

I'm hoping someone might have a clever solution to this problem, because at the moment, I'm stumped. I have a collectionView whose cell's all cast fairly large drop shadows onto the area behind them. The problem is that since they are so close together, each cell is also casting a shadow onto the immediately preceding cell. All of these cells have the same zIndex in their layoutAttributes, but Apple seems to be placing the cells with the higher indexPath values at a higher zIndex. Is there a good way to prevent these cells from casting their shadows onto the other cells?
The cells are setting their shadow via:
cell.layer.shadowOpacity = 1
cell.layer.shadowRadius = 54
cell.layer.shadowOffset = CGSize(width: 0, height: 12)
cell.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
Thanks!
Set the shadow properties of the collection view's layer, instead of setting them on each cell. Make sure the collection view has no backgroundView and that its backgroundColor is nil. Also make sure some ancestor view of the collection view does have a background color or some other fill.
Result:
Source code:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource {
var cellIdentifier: String { return "cell" }
override func loadView() {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .white
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 50, height: 50)
layout.minimumInteritemSpacing = 4
layout.minimumLineSpacing = 4
layout.scrollDirection = .vertical
layout.sectionInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = nil
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
collectionView.layer.shadowColor = UIColor.black.cgColor
collectionView.layer.shadowRadius = 8
collectionView.layer.shadowOpacity = 1
view.addSubview(collectionView)
self.view = view
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
let layer = cell.contentView.layer
layer.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
layer.borderColor = #colorLiteral(red: 0.4392156899, green: 0.01176470611, blue: 0.1921568662, alpha: 1)
layer.borderWidth = 2
layer.cornerRadius = 8
layer.masksToBounds = true
return cell
}
}
Well, I wasn't able to find a good way to prevent these shadows from casting onto the surrounding cells, but I think I found a working solution. I am going to add decoration views that layout directly underneath the cells, and then add the shadows to those views. It's a bit of a hack, but it should produce the effect I'm going for.

Why textView do not accommodate text kept inside it?

I have a plain textView and text inside it. With boundingRect i determine size based on text property of UITextView. Then i apply it to the size of my cell of collectionView. But text do not lay completely inside textView. but if i add to the width of the retrieved size 10 points everything works ok
Maybe i did not take something into account when was applying size to the cell?
Here i created my textView inside cell Class:
class experimentalCell: BaseCellInAppStoreFolder {
lazy var familySharingView: UITextView = {
let tv = UITextView()
tv.text = "Family Sharing"
tv.textAlignment = .right
tv.backgroundColor = UIColor.green
tv.textContainer.maximumNumberOfLines = 1
tv.font = UIFont.systemFont(ofSize: 11)
let options = NSStringDrawingOptions.usesLineFragmentOrigin
let dummySize = CGSize(width: 1000, height: self.frame.height - 16)
let rect = tv.text?.boundingRect(with: dummySize, options: options, context: nil)
tv.textContainerInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
return tv
}()
override func setupViews() {
super.setupViews()
addSubview(textView)
addConstraintsWithFormat("H:|[v0]|", views: textView)
addConstraintsWithFormat("V:|[v0]|", views: textView)
}
}
Then I applied size obtained form rect property to size of my cell.

iOS - subivew can't be centered in UITableViewCell

It's really weird.
No matter how I set the constraints, it just ignors all of them after layouts.
I've tried to use cell.indicator.center = cell.center and cell.indicator.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin] in the func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
but still not worked.
Is there any special way to center a subview inside a UITableViewCell?
LoadingCell.xib
Screenshot(Simulator)
updated:
After trying #Vibha Singh's advice, I put these codes in my LoadingCell.
override func layoutSubviews() {
print("layoutSubviews")
print(center)
print(contentView.bounds)
indicator.center = contentView.center
}
And it printed these lines:
layoutSubviews
(207.0, 1055.16668891907)
(0.0, 0.0, 414.0, 44.6666666666667)
But the indicator is still not centered.
updated:
I fixed it by creating a new cell with an indicator.
Both of them have exactly the same constraints. The new one is centered as expected, but the old one is still positioned at left top.
updated:
I did use centerX and centerY as constraints at the first time. But it's not worked. So I have to try another way. That's why I use so many constraints in the first screenshot.
Here are the screenshots with two exactly the same xibs.
Both of them use the same codes to dequeue.
let dequeue = tableView.dequeueReusableCell(withIdentifier: SharedCell.loading.rawValue, for: indexPath)
if let cell = dequeue as? CenterLoadingCell {
cell.indicator.startAnimating()
}
else if let cell = dequeue as? LoadingCell {
cell.indicator.startAnimating()
}
return dequeue
The first one is named LoadingCell, which is not centered on the simulator.
The second one is named CenterLoadingCell, which I created after I asked this question. And this one is centered on the simulator.
You are confusing your layout setting big time by adding unnecessary constraints. Check this implementation.
With Custom Cell:
You can add indicator like
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityView.center = CGPoint(x: CGFloat(cell.bounds.midX), y: CGFloat(cell.bounds.midY))
cell.contentView.addSubview(activityView)
activityView.startAnimating()
Or you can set frame in below method of cell
override func layoutSubviews()
{
}
You can make it easily with constraints
func setConstraints(item:UIView , relatedTo:UIView , attr:NSLayoutAttribute , relatedBy:NSLayoutRelation , multiplier: CGFloat , constant : CGFloat)
{
relatedTo.addSubview(item)
item.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: item, attribute: attr, relatedBy: relatedBy, toItem: relatedTo, attribute: attr, multiplier: multiplier, constant: constant).isActive = true
}
and usage :
setConstraints(item: indicator, relatedTo:cell.contentView, attr:centerX, relatedBy:.equal, multiplier: 0 , constant : 0)

Add subview on tableView cells causes a sad patchwork

I want to give a chat aspect to a table view of messages in my iPhone app.
In order to perform a quality render I add two subviews: the text and the "container" which is just a view with background color.
Even if it works the first time, when I scroll, it becomes really messy because it keeps adding lots of subviews.
Here you can see it when clean
And then when it has become messy
Here is the function to handle the transform, it's called when scrolling.
func configChatCell(cell: UITableViewCell, text: String, color:UIColor)
{
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
cell.textLabel?.textColor = UIColor.whiteColor()
let fixedWidth = cell.bounds.width - 150
let textView: UITextView = UITextView(frame: CGRect(x: 0, y: 0, width: 10, height: CGFloat.max))
textView.text = text
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = textView.frame
newFrame.size = CGSize(width: min(newSize.width, fixedWidth), height: newSize.height)
textView.sizeThatFits(newFrame.size)
textView.frame = newFrame
textView.backgroundColor = UIColor.clearColor()
self.rowHeight = textView.frame.height+20
let view = UIView()
view.backgroundColor = color
print(textView.frame.height+10)
view.frame = CGRect(x: 0, y: 5, width: textView.frame.width+50, height: textView.frame.height+10)
view.layer.cornerRadius = 5
cell.backgroundColor = UIColor.clearColor()
cell.contentView.addSubview(view)
cell.contentView.addSubview(textView)
cell.contentView.sendSubviewToBack(view)
}
If I remove the subviews each time I scroll, nothing appears on screen.
Can somebody help me to find a solution? Or is there any other way to do this?
Thanks in advance!
I quickly wrote up something for this.
It starts with the ChatCell
class ChatCell: UITableViewCell {
var messageLabel: UILabel? {
didSet {
messageLabel?.text = message
}
}
var message: String? {
didSet {
messageLabel?.text = message
}
}
class func messageCell(withText text: String, leading: Bool = true) -> ChatCell {
let cell = ChatCell()
cell.message = text
// Make the container
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(container)
container.topAnchor.constraintEqualToAnchor(cell.contentView.topAnchor, constant: 8).active = true
container.bottomAnchor.constraintEqualToAnchor(cell.contentView.bottomAnchor, constant: -8).active = true
if leading {
container.leadingAnchor.constraintEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintLessThanOrEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
} else {
container.leadingAnchor.constraintGreaterThanOrEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
}
// Make the messageLabel.
let messageLabel = UILabel()
messageLabel.numberOfLines = 0
messageLabel.textColor = UIColor.whiteColor()
messageLabel.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(messageLabel)
// Add constraints.
messageLabel.topAnchor.constraintEqualToAnchor(container.topAnchor, constant: 8).active = true
messageLabel.bottomAnchor.constraintEqualToAnchor(container.bottomAnchor, constant: -8).active = true
messageLabel.leadingAnchor.constraintEqualToAnchor(container.leadingAnchor, constant: 8).active = true
messageLabel.trailingAnchor.constraintEqualToAnchor(container.trailingAnchor, constant: -8).active = true
cell.messageLabel = messageLabel
container.backgroundColor = UIColor(red:0.19, green:0.70, blue:1.00, alpha:1.00)
container.layer.cornerRadius = 12.0
return cell
}
}
The cell also includes support for leading and trailing messages, for back and forth conversation. Perhaps make an array of tuples like this:
let messages: [(message: String, leading: Bool)] = [("Hello", true), ("My name is John Doe and this works quite well", false), ("I would agree", true)]
Then in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell you could do this:
let cell = ChatCell.messageCell(withText: messages[indexPath.row].message, leading: messages[indexPath.row].leading)
return cell
Let me know if this works for you. I tested it in a Playground and it works as expected
Assuming that your configureChatCell is called from tableView:cellForRowAtIndexPath:, then #Paulw11 is right; cells are reused, so you should only make changes that are unique to that row in the table. In your example, the only calls that you should be making in your method are textView.text = text and the ones to resize the textView to fit. Everything else should go in a dynamic cell prototype in the storyboard or, if you want to do everything in code (which I have a bad feeling you do), then put the rest of the configuration in a UITableViewCell subclass, then register that subclass with your table view.
I write something like this. It's simple but can elucidate solution
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseid", forIndexPath: indexPath)
// Configure the cell...
let contentView = cell.contentView
let subviews = contentView.subviews
for view in subviews {
if 100 == view.tag {
let label = view as! UILabel
label.text = self.datas[indexPath.row]
} else if 200 == view.tag {
view.layer.cornerRadius = 10.0
}
}
return cell
}
the key point is config every thing in tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
the view in code with tag 200 is a background view has same frame with label, I use layout constraint in storyboard to make sure its size and position.

Resources