I have UICollectionView with cells, that contains UIScrollView and inside it, there is a user defined UIView (via XIB)
I am using this code to init cell with data:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
for v in cell.contentView.subviews {
if let scroll: UIScrollView = v as? UIScrollView {
scroll.contentOffset = CGPoint(x: 0, y: 0)
for s in scroll.subviews {
if let content: DetailedView = s as? DetailedView {
content.fillData()
}
}
}
}
}
In my DetailedView, I have:
#IBDesignable
class DetailedView: UIView {
#IBOutlet weak var btnTemp: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// Performs the initial setup.
private func setupView() {
let view = viewFromNibForClass()
view.frame = bounds
// Auto-layout stuff.
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
}
// Loads a XIB file into a view and returns this view.
private func viewFromNibForClass() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
func fillData(){
self.btnTemp.titleLabel?.text = "btn temp"
print("fill data")
}
#IBAction func btnTempClick(_ sender: Any) {
print("Click")
}
}
All is working - view is visible, btnTempClick is called, but fillData is not working. It does not change content of button, there is still default text "Button". How to fix this?
Just to make it official removing it from comment and putting it as an answer.
have you tried using self.btnTemp.setTitle("btn temp", for:.normal)?
i think you can try with setTitle forControlState
Related
I have my custom cell 'NewsCell'. It contains my custom view 'ImageMosaicView' (that is just subclass of UIView). I use it to show photos like in post in Facebook. I just pass images' urls to ImageMosaicView's instance, and it loads it and shows.
I have an issue. When I scroll my tableView fast, then images from previous cell appear in new cell for a moment, while new images are loading. But I have no idea how they appear there, because I provided default images. Here is an example
How can I avoid this?
// ImageMosaicView.swift
class ImageMosaicView: UIView {
#IBOutlet var imageViews: [UIImageView]!
var urlStrings: [String] = [] {
didSet {
for imageView in imageViews {
if let url = URL(string: urlStrings[i]) {
imageView.loadImage(url: url)
}
}
}
// MARK: - Initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let _ = loadViewFromNib()
}
// MARK: - Methods
func loadViewFromNib() -> UIView {
let bundle = Bundle.init(for: type(of: self))
let nib = UINib(nibName: "ImageMosaicView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
return view
}
}
// How I provide urls for images:
// NewsViewController.swift. 'tableView(cellForRow:, indexPath:)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let news = newsCollection[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell", for: indexPath) as! NewsCell
//...
cell.imageMosaicView.urlStrings = news.imageUrlsCollection
//...
return cell
} else {
return UITableViewCell()
}
}
The right way to do this is to configure the prepareForReuse() method inside the NewsCell class.
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = //Default image
//Whatever other things you need cleared.
}
This method is called whenever a tableView dequeues a cell. So any cached data should be cleared here or it will persist in the dequeued cell unless you manually change it before returning it in the cellForRowAt method.
I'm trying to create the perfect solution to make a custom UIView subclass directly from code and in Storyboard. However, I didn't found any solution.
For an IBDesignable solution through storyboard, I followed this example http://supereasyapps.com/blog/2014/12/15/create-an-ibdesignable-uiview-subclass-with-code-from-an-xib-file-in-xcode-6 and works perfectly.
But if I try to call this subclass through UIViewController by calling this Extension method:
extension UIView {
class func loadFromNibNamed(nibNamed: String, bundle : NSBundle? = nil) -> UIView? {
return UINib(
nibName: nibNamed,
bundle: bundle
).instantiateWithOwner(nil, options: nil)[0] as? UIView
}
}
It crashes and says that misses this class is not key value coding-compliant for the key.
Has anybody found a solution to share with me for have both possibilities?
I need it because I should use this UIView in storyboard and also as UITableview SectionHeader
To preserve both cases, I preferred to write this inside my subclass declaration:
#IBDesignable class CustomView: UIView {
var view: UIView!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var label: UILabel!
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
}
In this way I can see and add it inside my storyboard.
In ViewForHeaderInSection method, I wrote this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = UIView()
let view:CustomView = CustomView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 30))
view.label.text = "header title: \(section)"
header.addSubview(view)
return header
}
And so it works ;)
Here's how you can use an xib in a view controller that is instantiated from storyboard (in your case as a section header of a UITableView).
Create an xib file, design your interface, and the necessary connections to of the UI elements with the IBOutlets in your swift file
In viewDidLoad method of your view controller class (one that is instantiated from the storyboard)
// Name your xib and swift class as Header
let headerNib = UINib(nibName: "Header", bundle: nil)
self.tableView.registerNib(headerNib, forHeaderFooterViewReuseIdentifier: "header")
Implement the UITableViewDelegate method viewForHeaderInSection
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("header") as! Header
// Customise your header view here
return header;
}
That's all you need to do.
I have an UITableViewCell contain a custom view, the custom view contains two label. The view hierarchical likes that:
|---Label 1
XIB---MyTableViewCell---MyView---|
|---Label 2
But run application, just shown 'MyView', Label 1 and Label 2 not visible! If I wrote code on viewDidLoad, take 'MyView' as viewController.view's subview, the label 1 and 2 is appeared. Hope you can help me.
Have you tried this?
Create MyView like this:
class MyView: UIView {
#IBOutlet weak var _label1: UILabel!
#IBOutlet weak var _label2: UILabel!
// Our custom view from the XIB file
var view: UIView!
//MARK: - Setup -
private func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
// Make the view stretch with containing view
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "MyView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override func awakeFromNib() {
super.awakeFromNib()
}
func setLabeles(lbl1: String, lbl2: String) {
_label1!.text = lbl1
_label2!.text = lbl2
}
}
Make Label1 and Label2 outlet to respective ones.
Then add it to custom tableViewCell as:
(Pink colour is for highlighting!)
Make My View outlet.
Custom cell code:
#IBOutlet weak var _myView: MyView!
class func identifier() -> String {
return "CustomTableViewCellId"
}
class func nib() -> UINib {
let nib = UINib(nibName: "CustomTableViewCell", bundle: NSBundle.mainBundle())
return nib
}
//MARK: Public Methods
func setCellIndexLabel(index: Int) {
_myView!.setLabeles("lbl1: \(index)", lbl2: "lbl2: \(index)")
}
Then there is no need to do extra anything in table view, just do:
In viewDidLoad()-
tableView!.registerNib(CustomTableViewCell.nib(), forCellReuseIdentifier: CustomTableViewCell.identifier())
//then
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier(CustomTableViewCell.identifier(), forIndexPath: indexPath) as! CustomTableViewCell
cell.setCellIndexLabel(indexPath.row)
return cell
}
And you will get this:
Is this what you are looking for?
Forgive me for providing swift code, but it's not much different that objective-c!
I'm trying to load an UITableView from a xib file. I created an UIView object which I use in order to load the xib file. Here I added an UITableView but with the last update of Xcode, I can't figure out how to include a prototype cell inside this table view.
Here there is my code of the UIView which loads the xib file:
class TopChartView: UIView, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableViewChart: UITableView!
var view : UIView!
var prova = [String]()
override init(frame: CGRect) {
super.init(frame: frame)
prova.append("hi")
prova.append("bye")
self.tableViewChart = UITableView()
self.tableViewChart.delegate = self
self.tableViewChart.dataSource = self
self.tableViewChart.registerClass(TopChartTVCell.self, forCellReuseIdentifier: "TopChart_IDcell")
//carico il file xib
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func xibSetup() {
Global.log.action()
view = loadViewFromNib()
// Make the view stretch with containing view
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
self.addSubview(view)
Global.log.action(methodEnded: true)
}
override func layoutSubviews() {
super.layoutSubviews()
Global.log.action()
let screenSize: CGRect = UIScreen.mainScreen().bounds
view.frame.size.width = screenSize.width
view.frame.size.height = screenSize.height
view.frame.origin.x = 0
view.frame.origin.y = 0
Global.log.action(methodEnded: true)
}
func loadViewFromNib() -> UIView {
Global.log.action()
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: Constants.topChartVC.xibFileName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
Global.log.action(methodEnded: true)
return view
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.prova.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TopChart_IDcell", forIndexPath: indexPath) as! TopChartTVCell
let elem = self.prova[indexPath.row]
cell.label.text = elem
return cell
}
}
And here there is my simple xib which is the problem because I'm not able to add an UITableViewCell inside the UITableView:
I have a problem, i have a controller, and in viewdidload, i try to load a subview created from a xib file.
My "custom" subview is well added to my first controller but the tableview isn't responding... i mean it doesn't scroll, and when i click it, nothing happens... (i haven't implemented yet the method to trigger an action when a cell is clicked, but the celle isn't highlighted when clicked).
Here the code :
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let v1 = MyViewTest(frame: CGRectZero)
v1.tableView.dataSource = self
v1.tableView.delegate = self
self.view.addSubview(v1)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//datasource method returning the what cell contains
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell")
//reusing the previous scrolled cells from table view(if any)
//cell.textLabel?.text = array[indexPath.row]
cell.textLabel?.text = "test"
//passing each elements of the array to cell
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//datasource method returning no. of rows
return 14
}
}
here the code in MyViewTest
class MyViewTest: UIView {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var view:UIView!
let nibName:String = "MyViewTest"
override init(frame: CGRect) {
super.init(frame:frame)
setup()
}
required init(coder aDecoder: NSCoder) {
//fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadViewFromNib()
let screenSize: CGRect = UIScreen.mainScreen().bounds
let sWidth = screenSize.width
let sHeight = screenSize.height
let xPos = sWidth/2-(view.bounds.width/2)
let yPos = sHeight/2-(view.bounds.height/2)
view.frame = CGRectMake(xPos, yPos, view.bounds.width, view.bounds.height)
addSubview(view)
}
func loadViewFromNib () -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let mview = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return mview
}
}
in the xib file, i only have a view with a label and my tableview in. The xib file is associated with its class (in identifier).
If you know why my table view isn't working it would be great thx !
You seem to set the frame of the MyTestView to CGRectZero.
You then add your table as a subview of this view with the frame size set up. As MyTestView has 0 width and height and the default for a view is to not clip subviews I imagine you can see the table but not click it.
Try setting the frame of your MyTestView to the screen size?
The problem happens because I instantiate my MyViewTest with CGRectZero. If i use a CGRectMake for example with actual width and height it works great.
See rory mckinnel post just above for more details :-)