iOS8 - UITableView + Search Bar + custom view for the header - xcode

I have UITableViewController with UISearchBarController.
I'm trying to add a custom view between the table and the search bar like this:
First of all, in iOS8 (not sure if it the same in previous versions) SearchBar is added as UITableView's header and adding additional view to the header removes the SearchBar.
Secondly, it is not possible to add a custom view for the FIRST section using viewForHeaderInSection like this:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let view = UIView()
view.frame = CGRectMake(0, 0, 320, 100)
return view
}
return nil
}
The code above doesn't produce anything. However, if I return the same view for section == 1 then I get a view displayed
Does anyone know any solutions for this? I see the only way to solve this by skipping the first section and display all the cells starting from the second section

Related

NSTableView reloadData(forRowIndexes:columnIndexes:) breaks autolayout

I have an NSTableView that can swap in different cell views based on data values for the row. When the model changes, I reload the table, and the table's delegate will provide the right table cell view for the new data.
The table uses autolayout for its cell views. All cell views load normally initially. When updating the table after a model change, I get different results depending on whether I call reloadData() or reloadData(forRowIndexes:columnIndexes). When using reloadData(), the cell view is loaded and autolayout works fine. If I use reloadData(forRowIndexes:columnIndexes), autolayout produces completely different, unexpected results.
I created a sample project to demonstrate the problem.
Here is an image of the project setup including constraints set on the table cell views. There are two row templates, one with a blue view (even rows), one with green (odd rows) that should span the table width (minus a bit of padding). A controller supplies the cell views:
class TableController: NSObject {
#IBOutlet weak var tableView: NSTableView!
var colorData = [1, 0, 1, 0]
#IBAction func swapLine(_ sender: Any) {
colorData[1] = (colorData[1] + 1) % 2
// tableView.reloadData()
tableView.reloadData(forRowIndexes: [1], columnIndexes: [0])
}
}
extension TableController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return colorData.count
}
}
extension TableController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cellId = (colorData[row]) % 2 == 0 ? "EvenCell" : "OddCell"
return tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(cellId), owner: self)
}
}
A button in the interface just swaps the data for row 1 and reloads the data. The initial view looks like this (alternating green and blue rects). If you use reloadData(), it looks like this (row 1 changed from blue to green). But, if you use reloadData(withRowIndexes:columnIndexes:), the cell view shrinks to 40 points wide vice 480 as in the others. Here's a grab of the view debugger showing the cell view with the wrong size and showing ambiguous width constraints (this doesn't happen when using reloadData()).
The documentation mentions that the row view is reused with reloadData(forRowIndexes:columnIndexes:), but not with reloadData(), which I've verified. I imagine this reusing of the row view is what's causing the autolayout problems, but I can find no connection. Nothing found at SO, AppKit release notes, WWDC videos, Google searches or from pounding my head on the table. Would be truly grateful for assistance.
Update:
Here's the code for ColorView:
class ColorView: NSView {
#IBInspectable var intrinsicHeight: CGFloat = 20
#IBInspectable var color: NSColor = NSColor.blue
override var intrinsicContentSize: NSSize {
return NSSize(width: NSView.noIntrinsicMetric, height: intrinsicHeight)
}
override func draw(_ dirtyRect: NSRect) {
color.setFill()
dirtyRect.fill()
}
}
I think I've got it working. If I call layoutSubtreeIfNeeded() on the cell just before it is returned (so that all its subviews like the dynamic text are already set), then it seems to work.
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
//...
cell.layoutSubtreeIfNeeded()
return cell
}
I hope that helps.
I ran into the same issue, and noticed the actual auto-layout constraints were missing for the rows that reloadData is called for. My (hacky) solution was to add the constraints that are supposed to be automatically set up for the cell manually as well. Note that in my table view I'm just using one column so I'm able to set the width constraint to equal the row's width instead of relying on the columns specified width.
class CustomRowView: NSTableRowView {
override func addSubview(_ view: NSView) {
super.addSubview(view)
// Add constraints NSTableView is supposed to set up
view.topAnchor.constraint(equalTo: topAnchor).isActive = true
view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0).isActive = true
view.layoutSubtreeIfNeeded()
}
}

How to prototype NSTableRowView in Interface Builder

I have a view-based NSTableView and I'm trying to customize the appearance of certain rows.
I understand I need to implement the delegate method mentioned in the title; However I'm not sure about how to do it.
The documentation says:
You can use the delegate method tableView:rowViewForRow: to customize
row views. You typically use Interface Builder to design and lay out
NSTableRowView prototype rows within the table. As with prototype
cells, prototype rows are retrieved programmatically at runtime.
Implementation of NSTableRowView subclasses is entirely optional.
However, unlike cells, there is no NSTableRowView class in interface builder, nor is it clear how to setup a "prototype" row view.
I am trying something like this (Swift 3):
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView?
{
if (row % 4) == 0 {
// ..................................................
// [ A ] SPECIAL ROW:
if let rowView = tableView.make(withIdentifier: "SpecialRow", owner: self) as? NSTableRowView {
rowView.backgroundColor = NSColor.gray()
return rowView
}
else {
return nil
}
// ^ Always returns nil (Because I don't know how
// to setup the prototype in Interface Builder)
}
else{
// ..................................................
// [ B ] NORMAL ROW (No customization needed)
return nil
}
}
I have similar code working for cells -i.e., -tableView:viewForTableColumn:row:.
OK, so the obvious (?) solution worked:
On Interface Builder, drag and drop a plain-vanilla NSView into the table (it will only accept the drop in a specific column, not as a direct child of the table view).
Go to the Identity Inspector for the just dropped view, and change its Class to "NSTableRowView".
Because just setting the .backgroundColor property as in my code does not work, I instead used this solution and added a box view as a child, and configured that in Interface Builder. I had to setup autolayout constraints between the box and the row view, so that it would stretch to the row view's actual size at runtime.
(Alternatively, I could have played with the wantsLayer property of the row view... )
UPDATE: It turns out the backgroundColor property I was using in my code is defined in NSTableRowView (NSView does not have such property, unlike UIView).
But it also gets overridden by the table view's setting (i.e., alternating rows or not), so instead I should customize it in this method:
func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int)
{
if (row % 4) == 0 {
rowView.backgroundColor = NSColor.controlAlternatingRowBackgroundColors()[1]
}
else{
rowView.backgroundColor = NSColor.clear()
}
}
...after it was added (and its background color configured by the table view).
(Incidentally, it turns out I do not need a custom row view after all. At least not to customize the background color)

Put a View in bottom of Screen

I try to put a View to the bottom of the screen on my UiTableView. How can I do that? Now it is still the last row. I searched for it but I didnt found someting useful. I hope you guys can help me
Please put UIView below the UITableViewCell like this.
Drag the desired view to the bottom of the UITableView's contents, i.e. below all of the table's cells.
You can add this line inside your code for your table view cell bottom
override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 60))
footerView.backgroundColor = UIColor.whiteColor()
return footerView
}

TableViewCell with UITextView not aligned and cutted until scolling

I've a TableViewCell with a UITextView, which content is not aligned and cutted at bottom at the first display:
When I scroll down and then up to the top, everything is fine:
My cellForRowAtIndexPath to get the content from a fetchedResultsController is simple:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TextViewCell") as! TextViewCell
let data = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
let text = data.valueForKey("textDu")!.description
cell.textContentView.text = text
return cell
}
How can I get the result after scrolling after start???
Use sizeToFit() after adding content to your textContentView.
cell.textContentView.text = text
cell.textContentView.sizeToFit()
Make sure for sizing cell
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "onContentSizeChange:",
name: UIContentSizeCategoryDidChangeNotification,
object: nil)
tableView.estimatedRowHeight = 89
tableView.rowHeight = UITableViewAutomaticDimension
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func onContentSizeChange(notification: NSNotification) {
tableView.reloadData()
}
Hope it helps you.
In conjunction with #Ashish Kakkad's answer you may want to try to set heightDimensions in viewDidLoad or viewWillAppear:
yourTableView.estimatedRowHeight = 30.0 // Put a real estimate here
yourTableView.rowHeight = UITableViewAutomaticDimension
Use auto layout code to tie the bottom of the cells contentView to the bottom of the text box. When the text box resizes it'll expand the cell with it.
This is in addition to Asish's correct suggestion about automatic cell heights and is quite a high level suggestion as you need to do a few things to get auto layout working right in tableview cells but there's ample examples on that out on the web.
oh, oh. Think I found something. I removed the existing contraints and then I've tried to "add missing constaints". The result was thas the error "Failed to automatically update constraints". Seem's I've a problem with my storyboard-file...

Textview caret/cursor is cut off at the bottom ios8

I have been struggling with what may be a bug in Xcode 6.0.1. I am building an app which has a similar scene as the apple compose section in the mail app. I am using
func textViewDidChange(textView: UITextView) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
which calls tableView.estimatedRowHeight and tableView.rowHeight = UITableViewAutomaticDimension in order to resize the cell accordingly using auto layout and simple constraints. I have cell.mytextview.delagete = self. This works flawlessly in my other scene where the text is is not entered by the user.
The problem is it appears that the cell caret/cursor is getting cut off when text reaches the keyboard or bottom of the screen. Also, if I press return repeatedly without entering text the caret/cursor disappears altogether for about 6 times until it scrolls me down to the bottom with the cursor still cut off. What I have realized is if I put 5 rows of text and then start back from rows enter text normally it works fine. Visually it looks similar to this. It just seems there is some gap where it is not auto scrolling to the correct place. I should also mention that the cell and textview are expanding because I can scroll down and see the text. I have already tried removing the other cells to see if they may have been causing the issue, but it had no effect. I have searched very hard on this and came up empty handed. Here is some relevant code.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellidentifier : String?
if indexPath.row == 0 {
cellidentifier = "tocell"
}
if indexPath.row == 1 {
cellidentifier = "subjectcell"
}
if indexPath.row == 2 {
cellidentifier = "bodycell"
}
let cell = tableView.dequeueReusableCellWithIdentifier(cellidentifier!) as composeTableViewCell
if indexPath.row == 0 {
cell.tofield!.text = toString
cell.tofield?.delegate = self
}
if indexPath.row == 1 {
cell.subjectfield?.delegate = self
}
if indexPath.row == 2 {
cell.bodyfield?.delegate = self
}
return cell
}
i hope this will use full until the Xcode fix the bug
use
enter code hereview.content = CGSizeMake(0,800) //like this and implement as per orientation

Resources