I'm trying to read data from a UIPickerView while is rolling.
Didselectedrow is giving you back row value only when UIPicker is not moving. How can I catch values from UIPickerView while is rolling?
I need to know exactly the passing data over the center row, get them and update other objects into UIView accordingly.
I tried to call a reading function using a timer, but didselectedrow does not update himself while rolling, see code below:
// define the timer
let cycleTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.readUIPicker), userInfo: nil, repeats: true)
as timer was defined it call reading func every 0.1s
// read UIPicker
func readUIPicker() {
let selectedRow = myPicker.selectedRow(inComponent: 0)
// Do something here based on selectedRow value
print("SelectedRow = \(selectedRow)")
}
The problem is that selectedRow will update only as soon as the rolling motion is stopped. No way to have control over passing elements.
While there is no API to get the picker's value while scrolling, the picker view will be calling your data source and delegate methods to get the title or view for a given row so it can be displayed as the user scrolls the picker view. So the best you can do is make use of the fact that the picker view just asked for the title or view of some row. But that row isn't necessarily the current center row.
Related
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!
I've disabled delaysContentTouches in my tableview subclass using:
delaysContentTouches = false
subviews.forEach { ($0 as? UIScrollView)?.delaysContentTouches = false }
But in one of my sections, I still want to keep the delay. Is there a way to cancel the delay for certain sections or perhaps I can add a custom recognizer delay to a section?
Sections are not actual objects within a tableView, so my answer to your first question is no. The .delaysContentTouches applies to the entire tableView.
For your second inquiry, I believe that one way it could be possible is through setting a delay for desired cells' scrollView subview. In your tableView(cellForRowAt: indexPath) func, you could have something like this:
if indexPath.section == 3 { //or whatever your desired section is
for view in cell.subviews {
if view is UIScrollView {
let currentView = view as! UIScrollView
currentView.delaysContentTouches = true
}
}
}
This will find the UIScrollView in your cell's subviews in your desired section. It will then set the .delaysContentTouches property accordingly.
I have not personally executed this code, just researched it, so let me know if it works.
Edit
Apparently the UIScrollView in UITableViewCell has been deprecated, so the above method will not work anymore.
My next best suggestion to you is to use a UILongPressGuestureRecognizer. This will not be quite the same thing as delaying the touch, but could have a similar effect in real execution.
You could use it in the same tableView(cellForRowAt: indexPath) func as so:
let press = UILongPressGestureRecognizer(target: self, action: #selector(handlePress))
press.minimumPressDuration = 2.0 //however long you want
cell.addGestureRecognizer(press)
Whatever you are trying to achieve by selecting certain rows of your tableView could be placed in the handlePress func above which would be trigged upon the long press.
I am trying to implement buttons with changeable background colors in Swift. But I do not want to change color of whole button. I want it to change like progress bar. For example, there will be a specific time value, and background color of button should change from right to left with animation in that time.
I saw apps like that. Can anyone give a clue for how to do that?
Don't be constrained by the idea that a button is only a UIButton. A button can be a view with a gesture recogniser, or a view with a transparent button over the top of it. It could be a button with a background image that you keep updating. There are many options for how to construct what you describe. It depends exactly what effect you want as to how it should be done. Buttons showing progress would often be disabled until progress is complete so you could easily use a progress indication view which is replaced by a 'real' button when progress is complete.
Maybe this helps you.
func changeButtonColors(){
//let myBtn
let timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "changeColor", userInfo: nil, repeats: true)
timer.fire()
}
//cloros to iterate over each time
let colorChoice = [UIColor.blackColor(), UIColor.blueColor(), UIColor.redColor()]
var counter = 0 //tracking variable
//this gets called when by the timer
//currently it is supposed to go on forever.
func changeColor(){
//suppose i have the button outlet named myBtn
UIView.animateWithDuration(2.0, animations: { () -> Void in
myBtn.backgroundColor = colorChoice[counter]
if counter < colorChoice.count{
counter++
}else{
counter = 0
//or disable the alert
//timer.invalidate()
}
})
}
Here myBtn is a outlet to the View Controller. We use Timer to trigger the function call at a specific time in our case every 2 second. This can be derived by other app logic.
The called function then iterates over a array of UIColor and applies one to the background of the button or view. The workflow is this. Change things where you require.
I've been playing around with the UITableView in swift.
Firstly, my goal is to append a cell with array data on click of the UIButton.
I've managed to do this with the following action
#IBAction func Option1Click(sender: UIButton) {
arrayOfQuestions.append(QuestionMark(Label:"Grocery List", option1:"yes", option2:"no")) }
But the issue is i cannot get to reload the tableView, because the tableView.reloadData() isn't inherited.
Secondly, i want to apply a timer. So that the appending of the cell would take a specific time interval.
I've managed to find a function on the forums
var timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self,
selector: Selector("Option1Click"), userInfo: nil, repeats: true)
But i don't know where exactly to place this code to work.
Would appreciate any insights greatly!
Thank you.
You should get a reference to your tableview as an outlet and name it whatever you like.
After you can use the reference of the tableview and call the reload method
like this:
nameofyourreference.reloadData()
I have a set of collectionviewcell in UICollectionView and i am using function NSTimer.scheduledTimerWithTimeInterval to command each cell to show up and move to the left in every 4 second. Currently, they are not in loop and it ends up blank on the screen once the last cell shows up. How can I create the loop for these cells and make them to move continuously? Please advise.
func timeSlide() {
var timeslide = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("onTime"), userInfo: nil, repeats: true)
}
func onTime(){
collection.contentOffset = CGPointMake(collection.contentOffset.x+375, 0)
}
You need to know how many items are there in the collection view, and when it gets to the end you need to set the contentOffset back to the beginning.