UIButton focus in UITableView - swift

I am trying to understand how the focus is working when I have button inside Table.
The goal is to create a table view with custom cell, that has the same size as the table view size ( I want to see only one cell at the time ). When the button is getting focused, I want to scroll ( the table view is doing it automatically ), to the next cell and to see only the next cell.
I made sure that the button will be in focus and not table view cell, by override this method in the Table cell:
override var canBecomeFocused: Bool{
return false
}
The button is focused and when I am swipe up/down, the focus is moving between the buttons and not the Table it self ( great success ) ... BUT, the table view is being scrolling automatically, and I want to understand why is some cases ( when the Table and Table Cell are bigger then 360 pixel ) it's scrolling to the next cell and I can see only the next cell ( the table view and the cell in the same size ) , and sometimes ( when the Table and Cell are Smaller then 360 pixel ) the table view scrolling just a little bit, until the next cell is showing only the button ( meaning the cell is not fully shown )
So , I have:
Cell and table view cell size are Greater then 360, we can see that when the button is focused, the table view scrolling to the center of the cell, and we can see only 1 cell:
Cell and table view cell size is Smaller then 360, we can see that when the button is focused, the table view scrolling to some point, when the button is shown
FYI: I think the size of the button is not effecting the scroll, only the table view and table view cell size is effect the scrolling.
Some one? any idea ?
This is the test Project: in order to see it better please change the table view size and the cell size to be smaller then 360, and make sure the UIButton is centered: Project

You can put those methods in UITableViewCell's subclass. see
override var preferredFocusEnvironments: [UIFocusEnvironment]{
// Condition
}
override func shouldUpdateFocus(in context: UIFocusUpdateContext) -> Bool {
// Condition
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
// Condition
}
you can see more at this link: enter link description here

Related

how to select contents of table cell on tvOS

I have a collectionView inside the table cells, but i can only click on the table cells.
Is there a way to disable user interaction on the table cells but enable it on the collectionCells inside the table cells, or a way to enter inside the table cells?
I show you an image below, where i cant click on the videos.
image
I am trying whit this and it works, but i cant scroll up the table view, i can only scroll down
public override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [collectionView]
}
Solved it by adding this code to my TableViewCell:
public override var canBecomeFocused: Bool {
return false
}

Remove a uiview once a table view cell is tapped (swift)

I have a custom xib table view cell that has a green uiview on it. The uiview acts as a new message indicator - whenever a message comes in, the green view is displayed. When a user taps the cell, I want to set the view to hidden. My only question is; how would I go about hiding the view just on that specific table view cell and not all table view cells?
Suppose your model
class Item {
var greenHidden = false
/// more properties
}
Then create the green view inside the xib and according to the current state do inside cellForRowAt
let cell = //
let item = arr[indexPath.row]
cell.greenView.isHidden = item.greenHidden
When the cell is tapped inside didSelectRowAt do
arr[indexPath.row].greenHidden = true
self.tableView.reloadRows(at:[indexPath],with:.none)

How to scroll UICollectionView cells before animated insert of new cell?

I have a horizontal scrolling UICollectionView to which I want to add multiple cells that will cause a 2 stage animation to occur:
UICollectionView scrolls existing cells to the left, showing
the empty space where the new cell will appear, then...
The new cell to be inserted then scrolls up vertically from off screen into the empty space.
Imagine the Cells as cards appearing from the bottom of the screen when a user triggers an action.
Currently I have a custom UICollectionViewFlowLayout with an overridden initialLayoutAttributesForAppearingItem which will animate the scroll up element of the transition, however after the first item this animation occurs offscreen. I cannot figure out how to scroll the UICollectionView to the left to show the space where the future cell will appear before the slide up animation occurs.
I have dabbled with contentOffset changes, however this results in some very jerky and glitchy behaviour.
Whats the best way to approach this?
Current `initialLayoutAttributesForAppearingItem' implementation:
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attr = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
attr?.alpha = 0.0
attr?.transform = CGAffineTransform(
translationX: 0,
y: self.collectionView!.bounds.height
)
return attr
}

Tracking the position of a NSCell on change

I have a NSTableView and want to track the position of its containing NSCells when the tableView got scrolled by the user.
I couldn’t find anything helpful. Would be great if someone can lead me into the right direction!
EDIT:
Thanks to #Ken Thomases and #Code Different, I just realized that I am using a view-based tableView, using tableView(_ tableView:viewFor tableColumn:row:), which returns a NSView.
However, that NSView is essentially a NSCell.
let cell = myTableView.make(withIdentifier: "customCell", owner: self) as! MyCustomTableCellView // NSTableCellView
So I really hope my initial question wasn’t misleading. I am still searching for a way how to track the position of the individual cells/views.
I set the behaviour of the NSScrollView (which contains the tableView) to Copy on Scroll in IB.
But when I check the x and y of the view/cells frame (within viewWillDraw of my MyCustomTableCellView subclass) it remains 0, 0.
NSScrollView doesn't use delegate. It uses the notification center to inform an observer that a change has taken place. The solution below assume vertical scrolling.
override func viewDidLoad() {
super.viewDidLoad()
// Observe the notification that the scroll view sends out whenever it finishes a scroll
let notificationName = NSNotification.Name.NSScrollViewDidLiveScroll
NotificationCenter.default.addObserver(self, selector: #selector(scrollViewDidScroll(_:)), name: notificationName, object: scrollView)
// Post an intial notification to so the user doesn't have to start scrolling to see the effect
scrollViewDidScroll(Notification(name: notificationName, object: scrollView, userInfo: nil))
}
// Whenever the scroll view finished scrolling, we will start coloring the rows
// based on how much they are visible in the scroll view. The idea is we will
// perform hit testing every n-pixel in the scroll view to see what table row
// lies there and change its color accordingly
func scrollViewDidScroll(_ notification: Notification) {
// The data's part of a table view begins with at the bottom of the table's header
let topEdge = tableView.headerView!.frame.height
let bottomEdge = scrollView.bounds.height
// We are going to do hit-testing every 10 pixel. For best efficiency, set
// the value to your typical row's height
let step = CGFloat(10.0)
for y in stride(from: topEdge, to: bottomEdge, by: step) {
let point = NSPoint(x: 10, y: y) // the point, in the coordinates of the scrollView
let hitPoint = scrollView.convert(point, to: tableView) // the same point, in the coordinates of the tableView
// The row that lies that the hitPoint
let row = tableView.row(at: hitPoint)
// If there is a row there
if row > -1 {
let rect = tableView.rect(ofRow: row) // the rect that contains row's view
let rowRect = tableView.convert(rect, to: scrollView) // the same rect, in the scrollView's coordinates system
let visibleRect = rowRect.intersection(scrollView.bounds) // the part of the row that visible from the scrollView
let visibility = visibleRect.height / rowRect.height // the percentage of the row that is visible
for column in 0..<tableView.numberOfColumns {
// Now iterate through every column in the row to change their color
if let cellView = tableView.view(atColumn: column, row: row, makeIfNecessary: true) as? NSTableCellView {
let color = cellView.textField?.textColor
// The rows in a typical text-only tableView is 17px tall
// It's hard to spot their grayness so we exaggerate the
// alpha component a bit here:
let alpha = visibility == 1 ? 1 : visibility / 3
cellView.textField?.textColor = color?.withAlphaComponent(alpha)
}
}
}
}
}
Result:
Update based on edited question:
First, just so you're aware, NSTableCellView is not an NSCell nor a subclass of it. When you are using a view-based table, you are not using NSCell for the cell views.
Also, a view's frame is always relative to the bounds of its immediate superview. It's not an absolute position. And the superview of the cell view is not the table view nor the scroll view. Cell views are inside of row views. That's why your cell view's origin is at 0, 0.
You could use NSTableView's frameOfCell(atColumn:row:) to determine where a given cell view is within the table view. I still don't think this is a good approach, though. Please see the last paragraph of my original answer, below:
Original answer:
Table views do not "contain" a bunch of NSCells as you seem to think. Also, NSCells do not have a position. The whole point of NSCell-based compound views is that they're much lighter-weight than an architecture that uses a separate object for each cell.
Usually, there's one NSCell for each table column. When the table view needs to draw the cells within a column, it configures that column's NSCell with the data for one cell and tells it to draw at that cell's position. Then, it configures that same NSCell with the data for the next cell and tells it to draw at the next position. Etc.
To do what you want, you could configure the scroll view to not copy on scroll. Then, the table view will be asked to draw everything whenever it is scrolled. Then, you would implement the tableView(_:willDisplayCell:for:row:) delegate method and apply the alpha value to the cells at the top and bottom edges of the scroll view.
But that's probably not a great approach.
I think you may have better luck by adding floating subviews to the scroll view that are partially transparent, with a gradient from fully opaque to fully transparent in the background color. So, instead of the cells fading out and letting the background show through, you put another view on top which only lets part of the cells show through.
I just solved the issue by myself.
Just set the contents view postsBoundsChangedNotifications to true and added an observer to NotificationCenter for NSViewBoundsDidChange. Works like a charm!

How to set height of tableview base on number of row its inserted

Let say I got TableView which got 3 rows.
When I insert record data from DB,it will turn 4 rows.
So,I don't want to see extra row because i set the tableview height.
If my tableView got 4 row,my tableView height is only for 4 rows and not for some extra blank space.
If it turn 3,then height for 3 row.
So,how to make table view height base on number row its inserted.
I am not talking about self-sizing table cell.
I am talking about changing the tableView height dynamically base on the records its inserted
You can make the table view taller within your view controller by changing the framesize, or the layout constraint.
That is, if your table is 3 rows... you can set your table height via it's frame.size.height, or you can adjust a layout constraint (which also can be set to an IBOutlet) to allow more space.
EDIT:
Here is how to do it in code (assuming you are using AutoLayout)
1 ) first add the constraints to your table view
2)
Add the IBOutlet to your view controller code.
3)
Connect the outlet in the storyboard by control clicking on the view controller object and moving the mouse to the bottom edge layout, like this:
4)
And then now that you have a layout outlet, you can adjust the constant for the bottom edge depending on how tall you want the table to be.
Here, I'm doing it when the view first appears. You'll need to customize this code depending on your needs.
Michael solution works like a charm , but I want to add that you can use the height constraint instead of the bottom and in the numberOfRowsInSection function
So you can multiply the row height with the number of rows
My table row height is 40, and the header height is 40
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var noOfRows = Int()
if case == 1 {
noOfRows = 5
} else if case == 2 {
noOfRows = 2
}
heightOfTable.constant = CGFloat(noOfRows) * 40 + 40 // +40 for the header height
return noOfRows
}