Autolayout constraints on custom UITableViewCell failing at runtime - ios

My app uses a table view controller to display a custom prototype table view cell containing image views and labels embedded in stack views. Unfortunately, at run time, the primary imageView (on the left) seems to abandon its constraints and display the image in an unanticipated manner.
While I have successfully implemented essentially the same configuration of components embedded in stack views in a view on the prior view controller (top), this same configuration doesn't seem to work in the custom table view cell (bottom).
This is how it appears in XCode:
This is how it appears in simulator:
In first diagnosing the issue, my debugger suggested I set up a symbolic breakpoint by adding a UIViewAlertForUnsatisfiableConstraint, since it was "attempting to recover by breaking a constraint", as described here: How to trap on UIViewAlertForUnsatisfiableConstraints?
That error disappeared after a few attempts at fixing the problem, and was replaced by what appears to be an Apple bug:
Is there a way around this latter bug or am I doing something to trigger it that I can fix?
FYI: I am mostly using IB to lay out constraints since the views are somewhat dense.

You are using UITableViewCell's default UIImageView.
You have to create Outlet connection for ImageView which is in UITableViewCell
Your Code:
class TableViewCell: UITableViewCell {
#IBOutlet weak var thumbImgVw: UIImageView!
}
if let url = URL(string: location.imageUrl) {
if let imageData = try? Data(contentsOf: url) {
let image = UIImage(data: imageData)
cell.thumbImgVw?.layer.cornerRadius = 10
cell.thumbImgVw?.clipsToBounds = true
cell.thumbImgVw?.image = image
}
}

Related

How to create multiple UIViews using the same function

I would like to create multiple UIViews that can be reproduced by using a single function. I have a UIView that is placed on a storyboard and connected to my class with an IBOutlet:
#IBOutlet weak var contentView: UIView!
I have a function that loads a xib into my UIView:
func createView(layoutConstant: CGFloat) {
if let customView = NSBundle.mainBundle().loadNibNamed("TestView", owner: self, options: nil).first as? TestView {
contentViewTopLayoutConstraint.constant = layoutConstant
contentView.addSubview(customView)
}
}
I am now trying to add two of them to my view, but only one shows up:
createView(0)
createView(70)
Any ideas?
I think both views are added, although they happen to be in the same spot, so it looks like there is only one! A quick and dirty way to verify that would be updating your createView method with this line:
contentView.frame.origin.y = layoutConstant
Basically your contentViewTopLayoutConstraint is not connected to the views you are creating, so setting its constant value will not have any impact.
Because frames for all those views will of same size. Origin(x,y) will be same for all the views, so they are overlapping one on another and you can only see the top one view.
In your code example it looks like you're only setting a layout constraint on the contentView you are placing your two new views inside of. What you need to do is set layout constraints on the two views your are placing inside in relation to their superview i.e. the contentView.
Basically, add the layout constraints to the customView views.
its quite simple.. iterate a loop by creating uiview along with adding those into the array and customize your particular view by getting them using array index.
Happy code ..

Using Autolayout constraints from Storyboard in UICollectionViewCell

I'm using a custom UICollectionViewCell class for my UICollectionView. I have to use addSubview in my custom class because I'm using FirebaseUI-iOS. This is what my MessageCollectionViewCell looks like:
import Foundation
import UIKit
class MessageCollectionViewCell: UICollectionViewCell {
#IBOutlet var messageContainerView: UIView?
#IBOutlet var messageText: UILabel?
#IBOutlet var messageDisplayName: UILabel?
#IBOutlet var messageUserImage: UIImageView?
#IBOutlet var messageUserImageOverlay: UIView?
override init(frame: CGRect) {
super.init(frame: frame)
// Custom initialization code for label
let size = self.contentView.frame.size
let frame = CGRectMake(0.0, 0.0, size.width, size.height)
self.messageContainerView = UIView(frame: frame)
self.messageUserImageOverlay = UIView(frame: frame)
self.messageText = UILabel(frame: frame)
self.messageDisplayName = UILabel(frame: frame)
self.messageContainerView!.addSubview(self.messageUserImageOverlay!)
self.messageContainerView!.addSubview(self.messageText!)
self.messageContainerView!.addSubview(self.messageDisplayName!)
self.contentView.addSubview(messageContainerView!)
}
required init(coder aDecoder: NSCoder) {
println("Init")
super.init(coder: aDecoder)
}
}
I have constraints in my Storyboard file which I want to use, but when I'm using addSubview my constraints does not get used. Is there anyway I can use addSubview() and still keep the constraints? I know that I can add it programmatically, but I wish to use the constraints that i've already set inside Storyboard.
This issue is due to how FirebaseUI 'FirebaseCollectionViewDataSource` registers it's classes. I don't believe it's possible to instantiate a class like this and get the autolayout properties from a XIB like you're asking, but it is possible to solve the problem a layer back by fixing our handling of prototype cells.
The problem here is that we're registering the cell reuseIdentifier twice: once in the storyboard and once in code (FirebaseCollectionViewDataSource must do this in order to dequeue cells). Since we call ours second, it overwrites the first one, which means that none of your outlets are populated, layouts are weird, etc. This means that you have to set them up as if you were using regular subclasses rather than XIBs. The quickest thing you could do here is just use a XIB instead of a prototype cell (which is just a XIB inside the storyboard). So, how can we support FirebaseUI + prototype cells...
The short answer is that currently this feature isn't possible due to Apple's design of UICollectionView.
Unlike UITableView, which can check for this behavior by dequeuing a cell (which would return an instantiated prototype cell and tell us that the reuseidentifier has already been created) like we do in FirebaseTableViewDataSource here, UICollectionView doesn't provide a similar method, it only gives:
func dequeueReusableCellWithReuseIdentifier(_ identifier: String,
forIndexPath indexPath: NSIndexPath!) -> AnyObject
Given that this method requires an a indexPath as it has to return a non-nil object, it will throw an NSInternalInconsistencyException when we try to read an arbitrary object at initialization (since there exist no items for it to read from). Additionally, there doesn't appear to be any way to programmatically check if a reuseIdentifier is in use. This leaves us with a few options:
Recommend people not use prototype cells and instead do custom subclasses or XIBs and hook them in. Storyboards and prototype cells feel a little more brittle (mostly for reasons like this), but definitely have ease of use going for them.
Pull the -registerClass: forReuseIdentifier: call out of FirebaseCollectionViewDataSource, though this means that FirebaseTableViewDataSource should be changed as well (even though it can work) and make the developer explicitly call this (or not in the case of using a Storyboard).
Add a parameter for storyboards to the initialization call which would still retain the reuseIdentifier to dequeue cells, but not register the class.
Try to dequeue a cell, catch the NSException, register the class and try again. This works, but it throws the exception still and adds a little more code in the runloop (we have to wrap the call with try-catch that we know will fail at most once).
My personal preference is 1, but the value proposition for prototype cells is high enough that 3 might be the best option for this library. I'd rather not do 2, since cell registration is a difficult enough problem.
For now, I'd recommend using XIBs instead of prototype cells, or waiting for us to pick one of the above solutions (we can push a release pretty quickly to solve the problem).

Xcode Error: Outlets cannot be connected to repeating content

After doing some searching and editing, I can't seem to find a solution to fixing this error. I'm trying to link my location search results with a table to display the search results in a list-form.
I have my map with the details button linked with a UIViewController called 'FirstViewController.' My results table is linked with a UITableViewController called 'ResultsTableViewController.' My prototype cells are linked with a UITableViewCell called 'ResultsTableCell' which is also where my outlets are located.
Here are the 2 separate errors:
Illegal Configuration: The nameLabel outlet from the ResultsTableViewController to the UILabel is invalid. Outlets cannot be connected to repeating content.
Illegal Configuration: The phoneLabel outlet from the ResultsTableViewController to the UILabel is invalid. Outlets cannot be connected to repeating content.
I've read other people's posts with the same problem, tried to fix them accordingly and I'm still getting the same error.
Here is the code for populating the cell, located in my ResultsTableViewController.
let cell = tableView.dequeueReusableCellWithIdentifier("resultCell", forIndexPath: indexPath) as! ResultsTableCell
// Configure the cell...
let row = indexPath.row
let item = mapItems[row]
cell.nameLabel.text = item.name
cell.phoneLabel.text = item.phoneNumber
return cell
}
The code in my ResultsTableCell class:
import UIKit
class ResultsTableCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var phoneLabel: UILabel!
}
This message only occurs if you connect it to the view controller. As I have already commented, you probably did not delete the first connection outlet you've made to your view controller. Even if you delete the IBOutlet code from your view controller you still need to right click it and delete the old connection that probably still there. After deleting it the error message will go away.
this issue happen when you delete view from your class but still have reference in your view
here is example I remove back outlet reference from my class but my view still keep the reference Notice yellow rectangle just delete it by click at x
if you want to know how to reach this view , open your storyboard , right click at top left yellow it will show this dialog

How to use a predefined view (nib) in a UITableViewCell?

My list consists of card views with an image and some text fields, all of which have a complex layout. This card is to be reused in the detail view, so containing it inside a UITableViewCell layout and then calling UITableView.registerNib() doesn't seem to work for me. I'd like to know the cleanest way to reuse the view I created in the NIB-file as both a list item and a part of my detail screen, while keeping as much of the view logic such as constraints and style in the NIB-file.
I'd like to know the cleanest way to reuse the view I created in the NIB-file as both a list item and a part of my detail screen
Do exactly what you're already doing: Use a nib (a .xib file) containing just one view, the UITableViewCell as designed, and register that nib with the table view - exactly the thing you say "doesn't seem to work for you". It does work. So now all your table rows consist of copies of this table view cell.
But there's the trick. When you want to show the view elsewhere, load the nib manually, pull out the view (the UITableViewCell), pull out its contentView, and add it as a subview to your interface.
// substitute correct name of xib file here
let arr = UINib(nibName: "MyTableViewCell", bundle: nil).instantiateWithOwner(
nil, options: nil)
let tvc = arr[0] as! UITableViewCell
let v = tvc.contentView // now it's a normal view! customize as needed
v.frame.origin = CGPointMake(100,100) // or whatever
self.view.addSubview(v) // and plop it into the interface! voilĂ !

TableView with Image SubView performance Issue

Ive got one question about using a subView in my custom TableViewCells. On certain rows i want to one or more images, and i am doing this programmatically with:
func addImageToCell(image: UIImage, initialYCoordinate: CGFloat, initialHeight: CGFloat, initialXCoordinate: CGFloat,imageUUID: String) {
imageButton = UIButton.buttonWithType(UIButtonType.Custom) as? UIButton
.....
imageButton!.addTarget(formVC, action: "imageButtonPressed:", forControlEvents: .TouchUpInside)
self.contentView.addSubview(imageButton!)
}
That works fine. But, when i scroll my TableView and it comes to an row with an Image, there is a small "lag". Normally it is totally smooth, but when he is trying to load this cell, there is a (maybe 0,1 seconds) lag in the software.
Is there a better way to do this programmatically without any lags? Ill load my Images on initializing the UITableViewController from a CoreData Fetch.
This is my cellForRowAtIndexPath
if(cellObject.hasImages == true) {
cell.qIndex = indexPath.row
var initialXCoordinate:CGFloat = 344.0
let questionImages = formImages[cellObject.qIndex] as [String: Images]!
for (key,image) in questionImages {
let thumbnailImage = UIImage(data: image.image)
cell.addImageToCell(thumbnailImage, initialYCoordinate: 44.00 ,initialHeight: cellObject.rowheight + cellObject.noticeHeight!, initialXCoordinate: initialXCoordinate, imageUUID: image.imageUUID)
initialXCoordinate += 130
}
}
Any Ideas? Thanks in advance
Edit: Ill use this to prevent the Buttons to get reused:
override func prepareForReuse() {
super.prepareForReuse()
if(self.reuseIdentifier != "CraftInit") {
for item in self.contentView.subviews {
if(item.isKindOfClass(UIButton)) {
item.removeFromSuperview()
}
}
}
The lag is most probably caused by the allocation of new memory for UIButton, adding it to cell's contentView and loading an image into it at the time of the cell being reused. That's one problem.
The other problem is that you're creating a button per every reuse of the cell. Basically, every time you scroll the cell out of the visibility range and scroll it back - you add another button with another image, that also consumes memory and performance.
To make this work properly you need to create a custom table view cell with a maximum amount of images (buttons) pre-rendered and hide the ones you don't need to show.
Do not add / remove subviews while reusing the table view cell, hide and show them instead, it's much faster.
Where are you removing these subviews? Because if you're not removing them, as the user scroll, you'll keep on adding subviews to the same cell over and over again, degrading performance.
My approach usually is to to have such views constructed when the cell is created rather than in cellForRowAtIndexPath, but I'm guessing you can't do that as you don't know the number of images. That said, you could still formulate a strategy where these subviews are added at cell creation, and then reused as per your model.
Another suggestion is to have the UIImage objects created all at once, outside cellForRowAtIndexPath.

Resources