Image not showing up in my UICollectionView in Swift - ios

My images not showing up in my UICollectionView. I'm not sure what is the mistake. Below is my code.
MyCollectionView.swift
/** -- **/
overide func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCollectionViewCell", forIndexPath: indexPath) as! MyCollectionViewCell
let event = events[indexPath.row]
cell.imageView?.image = UIImage(named: "blabla_iPad#2x")
return cell
}
/** -- **/
MyCollectionViewCell.swift
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
var imageView: UIImageView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
imageView = UIImageView(frame: CGRect(x: 0, y: 16, width: frame.size.width, height: frame.size.height))
imageView.contentMode = UIViewContentMode.ScaleAspectFit
contentView.addSubview(imageView)
}
}
Even I'm using localPath, it's still not working.
/*
/Users/MNurdin/Library/Developer/CoreSimulator/Devices/B30917DD-1C57-430F-9ACF-6526B226CF15/data/Containers/Data/Application/35ACB17A-4C25-4A49-BE3D-04AA51F36CC0/tmp/jreGfApABajWPCnkfPBh.jpg
*/
cell.imageView?.image = UIImage(data: NSData(contentsOfURL: NSURL(fileURLWithPath:localPath)!)!)

var imageView: UIImageView!
this should be an IBOutlet.
#IBOutlet weak var imageView: UIImageView!
And connect it to your storyboard.

Its quite the head scratcher. I don't know exactly whats wrong here, but here are a few tips i try myself to find whats happening
After:
cell.imageView?.image = UIImage(named: "blabla_iPad#2x")
print(cell.imageView?.image.bounds.size)
cell.imageView?.layer.borderWidth = 1.0
cell.imageView?.layer.borderColor = UIColor.blackColor().CGColor
u will see one of these things now:
1. same as before, this means u r drawing your image outside the cell. try changing the frame.
2. black bordered empty square: means something is wrong with your image
3. the frame printed has zero size. Assign the imageView frame equal to the bounds of the image.

Are you sure iamge is in Xcassets or in the top bundle.Save your image as "ImageName~ipad.png". It is just an identifier to ios to use this image on i-Pad. Put the image in Xcassets. Everything will work fine then.
And also put some log message in your custom cell class initializer to check the control flow,whether something is wrong with your custom class.
Have you registered your custom class to be used with this cell identifier in viewDidLoad?

You mentioned that you are loading your cell from your storyboard. When loading from storyboards, the init(frame: CGRect) method in your MyCollectionViewCell class won't get called, which means your code to add your custom UIImageView to the cell's contentView never gets called too.
Hence, the problem isn't to do with the image not loading into the UIImageView, but is more to do with the entire UIImageView not getting added into the cell at all.
Try moving that to the awakeFromNib() instead

I had the same issue. Only happened on xib files I opened after switching to Swift 3. I disabled Clip To Bounds on any UIImageView in the xib and I can see the images again, but now my cornerRadius settings don't work.
Also had an issue when setting cornerRadius based on view size in awakeFromNib because xib views default with a frame size of (1000,1000).

Related

Embed Custom UITableViewCell within another custom UIView?

I have a UITableViewController with a list of custom UITableViewCells. The cell has text, which is sometimes long. I am truncating that at 2 lines in the table.
When a user touches the cell, I want to create a UIView as a popup with the exact same information as the UITableViewCell, with the addition of a header above the UITableViewCell content. So, essentially, I want something like the following:
I have built VersePopupView just as indicated in the picture, where the text "Custom Header" is actually a UILabel. I have instantiated the view from the XIB file and successfully set the UILabel text, but the custom UITableViewCell doesn't show, even though I have assigned that value as well. My IBOutlets are all hooked up to IB.
Here is my instantiation:
let popupView: VersePopupView = VersePopupView.instanceFromNib()
popupView.frame = CGRect(x: 10, y: 100, width: 300, height: 100)
popupView.assignVerseTableViewCell(cell: cell)
window.addSubview(popupView)
window.makeKeyAndVisible()
And here is my VersePopupView class:
class VersePopupView: UIView {
#IBOutlet weak var cell: VerseTableViewCell!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var headerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
class func instanceFromNib() -> VersePopupView {
return UINib(nibName: "VersePopupView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! VersePopupView
}
func assignVerseTableViewCell(cell: VerseTableViewCell)
{
// Assign it
self.cell = cell
// Get the verse details
if let verseDetails = cell.verse_details {
self.title.text = verseDetails.verseReference()
// Adjust the frame
// Adjust the text to be attributed text
// Set the title to the verse reference
// Anything else?
}
}
}
What am I doing wrong such that the UITableViewCell isn't showing up?
Is there a better pattern for this rather than embedding the UITableViewCell?
I don't want to duplicate code (which I have done in the past), because I want the user to interact with the popup just like they would in the table cell.
I'm not sure how to convince you of how easy this is to do without trying to misuse a table view cell, so here's a screen shot:
You just have to believe me when I say that that's a three-row table followed by a totally independent ordinary view plucked from the inside of the table view cell (by loading the cell nib).
They not only look identical, they act identical; the view is a custom UIView subclass with an action method from the switch, and when I click the switch, it triggers that action method, both within the table and in the view outside it.
The view was designed entirely in the nib, and absolutely no code was repeated.
That seems to be the sort of thing you want to do. So, I encourage you, don't misuse table view cells; do this with an ordinary view that can live happily inside a cell or outside it.

Set rounded corners on UIimage in UICollectionViewCell in swift

I have a simple problem that I cannot find a solution to on google, the docs or here.
I have a Collectionview in my view controller. I have created a custom cell, DescriptionCell, that contains a UIImage. I want this image to have rounded corners. However, I don't know where to set the cornerradius on the UIImage layer. I have tried in the cells' awakeFromNib method, in the delegate method CellForRowAtIndexPath and overriden LayoutSubview in the cell but it does not work.
Where should I put the code to set the radius for the UIImage?
To specify, I know how to create rounded corners of a UIImage. But if it is a subview of a Collectionview cell, I do not know where to set the cornerradius.
Here is code for my descriptionCell
class DescriptionCell: UICollectionViewCell {
#IBOutlet weak var mImage: UIImageView!
override func awakeFromNib() {
//mImage.layer.cornerradius = 5
//Does not work, the image is still square in the cell
}
And in the cellforRowAtIndePath
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("descriptioncell", forIndexPath: indexPath) as! DescriptionCell
//cell.mImage.layer.cornerradius = 5
//Does not work, the image is still square in the cell
return cell
}
Thanks in advance!
Well you're using part of the code from the answer you said you were using.
the other part is imageView.clipsToBounds = true
Update your awakeFromNib like this:
override func awakeFromNib() {
mImage.layer.cornerRadius = 5
mimage.clipsToBounds = true
}
To make it a circle you need to set cornerRadius to half of the square height. In your cellForItemAtIndexPath add these lines:
cell.layoutIfNeeded()
cell.mImage.layer.cornerRadius = cell.mImage.frame.height/2
Update
To avoid layoutSubviews from being called twice, override layoutSubviews in your DescriptionCell class and put the code there:
override func layoutSubviews() {
super.layoutSubviews()
layoutIfNeeded()
mImage.layer.cornerRadius = mImage.frame.height/2
}
Have you tried placing it inside the custom UICollectionViewCell's init function?
override init(frame: CGRect) {
super.init(frame: frame)
image.layer.masksToBounds = true
image.layer.cornerRadius = 10
}
You can also create an extension like:
extension UIView {
func addBorder(color: UIColor, cornerRadius: CGFloat = 10, borderWidth: CGFloat = 1.0) {
layer.borderWidth = borderWidth;
layer.borderColor = color.cgColor
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}

How do you programmatically make UIViews which are the height of textLabels inside them? (In Swift)

I'm trying to make UIViews which each contain different statements of text (In UITextViews). There can be a varying number of views and each statement can be different in length. I make these views using
let newView = DragView(heightOfView: ???, viewNumber: i, heightFromTop: currentHeightForThings)
In the DragView class I then access the statement using the viewNumber and put the statement in the text label in a nib file I've made.
My issue is I have nothing to put in heightOfView. The height I want is the height of the textLabel which varies depending on how many lines are in the textView for the statement. However I can't access this height because the textLabel isn't built yet.
Thanks in advance, I'm new to swift but want to learn fast so I apologise if I'm missing something obvious!
Heres the code I have in the class DragView
class DragView: UIView {
#IBOutlet var dragView: UIView!
#IBOutlet weak var statementLabel: UITextView!
var dropTarget: UIView?
var viewNumber: Int!
init(heightOfView: Int, viewNumber:Int, heightFromTop: Int) {
self.viewNumber = viewNumber
let startingPosition = CGRect(x: Int(widthCentre) - dragViewWidth / 2, y: heightFromTop, width: dragViewWidth, height: heightOfView)
super.init(frame: startingPosition)
NSBundle.mainBundle().loadNibNamed("DragView", owner: self, options: nil)
self.addSubview(self.dragView)
let movingView = MovingView(frame: startingPosition)
self.addSubview(movingView)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Here the movingView is a subview I add over the view to move the view.
You can override the DragView's sizeThatFits method to have it return a height based on the statementLabel's height. You will have to first call sizeToFit on the textView which will set the height for that view, then return a height based on that.
override func sizeThatFits(size: CGSize) -> CGSize {
self.statementLabel.sizeToFit()
return CGSize(
width: self.frame.width,
height: self.statementLabel.frame.height)
}
Additionally, I would recommend looking into sizeThatFits and layoutSubviews if you are going to be doing programatic layout. The sizing and positioning of subviews should be taking place in layoutSubviews rather than init.

Frame doesn't change properly unless is equal to UIView frame

I have some weird behavior related to frame sizes that I can't fix after hours of trying different things. This just doesn't make any sense.
I have a custom UIView and a related .xib file:
ErrorView.swift
import UIKit
class ErrorView: UIView {
#IBOutlet weak var labelErrorTitle: UILabel!
#IBOutlet weak var view: UIView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
NSBundle.mainBundle().loadNibNamed("ErrorView", owner: self, options: nil)
self.addSubview(view)
}
ErrorView.xib (using inferred size, the label is centered using constraints)
I want to add this view to a custom UITableView, at the bottom. I want to make it slim, with a height of 45 and a width of the view screen width. I want to add it to the bottom.
Very easy!! I just set the size with a frame like this:
class LoadingTableView: UITableView {
var errorView: ErrorView = ErrorView () // Here is the Error View
var errorFrame: CGRect! // The frame (size) I will use
required init(coder aDecoder: NSCoder) {
}
override func awakeFromNib() {
super.awakeFromNib()
// I create the frame here to put the error at the top
errorFrame = CGRectMake(0,0,self.frame.width,45)
// Init the ErrorView
errorView = ErrorView(frame: errorFrame)
// I add the subview to root (I have this rootView created)
rootView?.addSubview(errorView)
}
// This is needed, it updates the size when layout changes
override func layoutSubviews() {
super.layoutSubviews()
// Create the size again
errorFrame = CGRectMake(0,0,self.frame.width,45)
// I update the frame
errorView.frame = frame
}
This should work but it doesn't. The size is just weird. It takes the size from the nib which is 320x568. Then it just moves the frame, but the size doesn't change.
And here comes the great part. If I set the errorView.frame size to .frame, which is the frame of the tableView then it works! With orientation changes and all!
But as long as I change the frame to a custom size, whatever is in awakeFromNib or layoutSubviews it doesn't and starts to act weird.
Why does it keeps the size of the nib? And why if I put .frame it works at it should but a custom size doesn't? It looks like I'm super close, it's frustrating as hell.
The objective is to say tableView.error("errorCode") and then that errorView appears. And it works on all devices and orientations.
Instead of adding a subview to UITableView, use it's tableFooterView and tableHeaderView properties:
Replace
rootView?.addSubview(errorView)
With:
tableFooterView = errorView

Subview frame is incorrect when creating UICollectionViewCell

The problem
I created a UICollectionViewController with a custom UICollectionViewCell.
The custom cell contains a large and rectangular UIView (named colorView) and a UILabel (named nameLabel).
When the collection is first populated with its cells and I print colorView.frame, the printed frames have incorrect values. I know they are incorrect, because the colorView frames are larger than the cell frame themselves, even though the colorView gets drawn correctly.
However, if I scroll the collectionView enough to trigger a reuse of a previously created cell, the colorView.frame now has correct values!
I need the correct frames because I want to apply rounded corners to the colorView layer and I need the correct coloView size in order to do this.
By the way, in case you are wondering, colorView.bounds also has the same wrong size value as the colorView.frame.
The question
Why are the frames incorrect when creating the cells?
And now some code
This is my UICollectionViewCell:
class BugCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var colorView: UIView!
#IBOutlet weak var nameLabel: UILabel!
}
and this is the UICollectionViewController:
import UIKit
let reuseIdentifier = "Cell"
let colors = [UIColor.redColor(), UIColor.blueColor(),
UIColor.greenColor(), UIColor.purpleColor()]
let labels = ["red", "blue", "green", "purple"]
class BugCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return colors.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as BugCollectionViewCell
println("ColorView frame: \(cell.colorView.frame) Cell frame: \(cell.frame)")
cell.colorView.backgroundColor = colors[indexPath.row]
cell.nameLabel.text = labels[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width = self.collectionView?.frame.width
let height = self.collectionView?.frame.height
return CGSizeMake(width!, height!/2)
}
}
The collection view is setup in order to show two cells at a time, vertically, each cell containing a large rectangle painted with a color and a label with the color name.
When I just run the above code on the simulator, I get the following printed result:
ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,0.0,375.0,333.5)
ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,343.5,375.0,333.5)
It is a weird result - colorView.frame has a height of 568 points, while the cell frame is only 333.5 points tall.
If I drag the collectionView down and a cell gets reused, the following result is printed:
ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,1030.5,375.0,333.5)
ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,343.5,375.0,333.5)
Something, which I can’t understand, happened along the way that corrects the frame of colorView.
I think it has something to do with the fact that the cell is loaded from the Nib, so instead of using the init(frame: frame) initializer the controller uses the init(coder: aCoder) initializer, so as soon as the cell is created it probably comes with some default frame which I can't edit anyhow.
I’ll appreciate any help that allows me to understand what is happening!
I am using Xcode 6.1.1. with the iOS SDK 8.1.
You can get the final frames of your cell by overriding layoutIfNeeded() in your custom Cell class like this:
override func layoutIfNeeded() {
super.layoutIfNeeded()
self.subView.layer.cornerRadius = self.subView.bounds.width / 2
}
then in your UICollectionView data Source method cellForRowAtIndexPath: do this:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.setNeedsLayout()
cell.layoutIfNeeded()
I had the same issue with a UICollectionViewCell using auto layout constraints.
I had to call layoutIfNeeded before I was configuring my subview that relied on the views frame width.
Had this issue with Core Graphics drawing in iOS 10, Swift 3.0.1.
Add this method to UICollectionView subclass:
override func didMoveToSuperview() {
super.didMoveToSuperview()
setNeedsLayout()
layoutIfNeeded()
}
My problem was that Core Graphics shapes were not calculated properly, because a layoutSubviews() wasn't called.
Ok, I understand now that the cell is created before auto layout defines its frames. That is the reason why at the moment of creation the bounds are wrong. When the cells are reused the frames have been already corrected.
I was having this problem while creating a custom UIView that placed some layers and subviews in specific coordinates. When instances of this UIView were created, the placement of the subviews were all wrong (because auto layout hadn't kick off yet).
I found out that instead of configuring the view subviews on init(coder: aCoder) I had to override the method layoutSubviews(). This is called when auto layout asks each view to layout its own subviews, so at this point at least the parent view has the correct frame and I can use it for laying the subviews correctly.
Probably if I had used constraints on the custom view code instead of dealing myself with frame sizes and positioning then the layout would have been done properly and it wouldn't be necessary to override layoutSubviews().
I'd suggest making a subclass of whatever you're doing. I needed a gradient over an UIImageView in my cell and it was calculating it wrongly. I tried the suggestion with layoutSubviews but it was also causing issues where it seems like it would apply gradient twice.
I made a UIImageView subclass and it works as wanted.
class MyOwnImageView: UIImageView{
override func layoutSubviews() {
super.layoutSubviews()
let view = UIView(frame: frame)
let width = bounds.width
let height = bounds.height
let sHeight:CGFloat = 122.0
let shadow = UIColor.black.withAlphaComponent(0.9).cgColor
let topImageGradient = CAGradientLayer()
topImageGradient.frame = CGRect(x: 0, y: 0, width: width, height: sHeight)
topImageGradient.colors = [shadow, UIColor.clear.cgColor]
view.layer.insertSublayer(topImageGradient, at: 0)
let bottomImageGradient = CAGradientLayer()
bottomImageGradient.frame = CGRect(x: 0, y: height - sHeight, width: width, height: sHeight)
bottomImageGradient.colors = [UIColor.clear.cgColor, shadow]
view.layer.insertSublayer(bottomImageGradient, at: 0)
addSubview(view)
bringSubviewToFront(view)
}
}

Resources