This question already has an answer here:
How to identify the viewController a button has been added onto?
(1 answer)
Closed 3 years ago.
I have a problem in a TableView. This is the class which contains the TableView.
class LogViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
…
func loadLogList() – > [String] {
…
}
The list contains all Logfiles in my Log file directory. In a custom TableViewCell I insert a button which deletes the Logfile. This is the user defined tableViewCell:
class LogTableViewCell: UITableViewCell {
…
#IBAction func deletePressed(_ sender: Any) {
let fileManager = FileManager.default
do {
try fileManager.removeItem(atPath: logURL!.path)
}
catch let error as NSError {
print("Fehler: \(error)")
}
}
After pressing the button the dependent file will be removed. Now I want to refresh this list. But the Button is in the TableViewCell class and the function for refreshing the list is in the TableView class. How can I refresh the tableview and the corresponding array?
You may use Notification Observer pattern for this, but you can access the tableView of the cell with this extension:
extension UITableViewCell {
var tableView: UITableView? {
return (next as? UITableView) ?? (parentViewController as? UITableViewController)?.tableView
}
}
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
- So now:
you can simply do tableView?.reloadData() inside the cell.
Related
My code below deletes a string from the index via delete button. However it is not delete from the core data set. If view is refreshed all items in the set are still there. All I want is when the user hits the delete func that that string is deleted forever. This is a download link to my code https://www.dropbox.com/s/m5fpiilksye4pru/photo2.zip?dl=0
var users = [Item]()
class cell: UICollectionViewCell {
var delegateA: datacollectionProfotocol?
var index : IndexPath?
#IBOutlet var name : UILabel!
#IBAction func show() {
}
#IBAction func delete() {
delegateA?.deleteData(indx: (index?.row)!)
}
}
protocol datacollectionProfotocol {
func deleteData(indx:Int)
}
extension ViewController : datacollectionProfotocol {
func deleteData(indx: Int) {
users.remove(at: indx)
block.reloadData()
}
You have to delete the item also from the Core Data stack (context is the NSManagedObjectContext instance)
func deleteData(indx: Int) {
let userToRemove = users.remove(at: indx)
context.delete(userToRemove)
do {
try context.save()
block.reloadData()
} catch { print(error) }
}
If I have this extension:
extension UIResponder {
var parentViewController: UIViewController? {
return (self.next as? UIViewController) ?? self.next?.parentViewController
}
}
And this one:
extension UIView {
var getParentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
If I have a class like this:
class MyClass: UIView {
init() {
print("\( parentViewController == nil )")
print("\( getParentViewController == nil )")
}
}
Both results are null.
How I can get the implemented ViewController from my UIView without pass it as parameter (directly from the view)?
You can’t get the view’s next responder in its init because it hasn’t been set yet. An appropriate time would be in didMoveToWindow.
Also, in your own answer, you said “…is the easier way to get the uiviewcontroller”, as if an app has only one instance of UIViewController, but every non-trivial app has multiple view controllers.
I'm working on an NSOutlineView that uses NSView subclasses to generate custom cells in the outline. This I've gotten to work, BUT after the Outline sucks in the data from the model class and displays it correctly, the Outline is released(?) from memory / goes to nil and I haven't figured out a way to get it back.
Here is the MainViewController class
class MainWindowController: NSWindowController, ShareInfoDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource {
override var windowNibName: String {
return "MainWindowController"
}
#IBOutlet var daOutline: NSOutlineView!
// The NSoutline I'm trying to get back to
Some stuff related to the test data (Omitted)
leading us to the NSOutlineViewDataSource stuff
//MARK: - NSOutlineViewDataSource
func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
if let item: AnyObject = item {
switch item {
case let work as Work:
return work.movements[index]
case let movement as Movement:
return movement.tracks[index]
default:
let track = item as! Track
return track.credits[index]
}
} else {
if allWorks.count > 0 {
return allWorks[index]
}
}
let q = "patience"
return q
}
func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
switch item {
case let work as Work:
return (work.movements.count > 0) ? true : false
case let movement as Movement:
return (movement.tracks.count > 0) ? true : false
case let track as Track:
return (track.credits.count > 0) ? true: false
default:
return false
}
}
func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
if let item: AnyObject = item {
switch item {
case let work as Work:
return work.movements.count
case let movement as Movement:
return movement.tracks.count
case let track as Track:
return track.credits.count
default:
return 0
}
} else {
return allWorks.count
}
}
func outlineView(daOutline: NSOutlineView, viewForTableColumn theColumn: NSTableColumn?, item: AnyObject) -> NSView? {
switch item {
case let worked as Work:
let cell = daOutline.makeViewWithIdentifier("newTry", owner:self) as! newTry
cell.fourthLabel.stringValue = worked.composer
cell.fourthCell.stringValue = worked.title
return cell
case let moved as Movement:
let cell2 = daOutline.makeViewWithIdentifier("SecondTry", owner:self) as! SecondTry
cell2.roman.stringValue = moved.name!
cell2.details.stringValue = moved.sections!
cell2.track.stringValue = "0"
return cell2
default:
print("probably not")
}
print("not again")
return nil
}
func outlineView(daOutline: NSOutlineView, heightOfRowByItem item: AnyObject) -> CGFloat {
switch item {
case let worked as Work:
return 40
default:
return 24
}
}
And the stuff in WindowDidLoad
override func windowDidLoad() {
super.windowDidLoad()
let nib = NSNib(nibNamed: "newTry", bundle: NSBundle.mainBundle())
daOutline.registerNib(nib!, forIdentifier: "newTry")
let nib2 = NSNib(nibNamed: "SecondTry", bundle: NSBundle.mainBundle())
daOutline.registerNib(nib2!, forIdentifier: "SecondTry")
//give Sender it's Receiver
mailItOut.delegate = receiver
allWorks.append(work1)
allWorks.append(work2)
work1.movements.append(move1)
work1.movements.append(move2)
work1.movements.append(move3)
work1.movements.append(move4)
work2.movements.append(move5)
work2.movements.append(move6)
work2.movements.append(move7)
daOutline.reloadData()
daOutline?.expandItem(work1, expandChildren: false)
daOutline?.expandItem(work2, expandChildren: false)
}
}
And Finally what the newTry NSView class looks like
class newTry: NSView {
var delegate: ShareInfoDelegate?
#IBOutlet weak var fourthCell: NSTextField!
#IBOutlet weak var fourthLabel: NSTextField!
#IBAction func cellAdd(sender: NSTextField) {
var catchIt: String = String()
catchIt = sender.stringValue
if catchIt != "" {
tryAgain = catchIt
whichField = "title"
//Trigger the sender to send message to it's Receiver
mailItOut.sendMessage()
}
}
The cellAdd Action is used to try and get user input from the text cells back into the model. To do this I (AFAIK) need to access the NSOutline (daOutline) and get which row I'm at and put the data from the sender into the appropriate part of the Model class. Which is something that I've managed to get to work in a standard (1 cell / 1 data value) outline. But in this prototype, as far as I can tell, the MainWindowController has released all of its contents and daOutline is nil (bad).
How do I get XCode to bring / reload the completed outline (or never release it) and get daOutline to a non nil state?
For those who come after there appeared to be two problems that led to the NSOutline outlet becoming nil. The first one was that in implementing the delegate protocol "shareInfoDelegate" I was creating a new instance of the MainWindowController, not the one with the data in it. This new instance did NOT have the IBOutlets connected (or much of anything useful about it).
Once I scrapped the Delegate and moved to using NSNotification to update information about the NSView textFields my NSOutline came "back".
The second, more minor, problem was that in the NSView nib file I placed and NSBox to mimic the behavior of a group row (e.g. a gray background). As a side effect the NSBox was inhibiting the normal row select behavior of the outline. Which made it very hard to determine which row was selected. When I deleted the NSBox, row selection became much more easy to determine.
in particular this Question and the answer by Chuck were helpful in sniffing this out.
Why is my NSOutlineView datasource nil?
Thanks Indeed(!)
I have a custom popup view that has a UIDatePicker. This, when changed, changes the date of the save time. I also want the label on the in the CustomCell to be updated if the date has changed. I have used a delegate protocol to update the table but I cannot get this protocol to transfer the information on save. Can you help? I think I have hooked up all the correct code in the viewController class. I have tried this answer but I cannot set the delegate in the target class and there isn't a segue A Swift example of Custom Views for Data Input (custom in-app keyboard)
protocol DatePopUpViewDelegate: class {
func pastDate(date: String) // date that is chosen in picker
func isPastDateSet(isSet: Bool) // has chosen new date
}
#IBDesignable class DatePopUpView: UIView {
var delegate: DatePopUpViewDelegate?
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "DatePopUp", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
delegate?.isPastDateSet(false)
return view
}
// close popup
#IBAction func closeButtonDatePopUp(sender: AnyObject) {
if dateToSave != openTime {
if let dateToSave = dateToSave {
SaveData.changedSaveTime = dateToSave
delegate?.pastDate(dateToSave)
delegate?.isPastDateSet(true)
}
} else {
SaveData.changedSaveTime = ""
delegate?.isPastDateSet(false)
}
}
class SaveTableViewCell: UITableViewCell, DatePopUpViewDelegate {
var changeDateLabel: Bool = false
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
changeDateLabel = false
datePopUpViewControllert.delegate = self
}
// delegate functions
func pastDate(date: String) {
self.labelDate = date
print("del date \(date)")
}
func isPastDateSet(isSet: Bool) {
self.changeDateLabel = isSet
print("is set by delegate \(isSet)")
}
I have a cell which I use in many different vc because I have my app divided in different categories but all use the same cell.
Now I have a button which should trigger the event to share it via other apps like whastapp or facebook.
The problem is that depending on in which category you are you have a different view controller which will display the function.
I can make it work with one but not with 10 different vc in just one cell.
I used an extension to get the parentviewController
extension UIView {
var parentViewController: HomeViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if parentResponder is UIViewController {
return parentResponder as! HomeViewController!
}
}
return nil
}
This will obviously only work at the Home vc.
How can I work around this issue?
You can use a protocol to handle your button action in the view controller:
protocol ShareEventDelegate: class {
func didShareButtonSelected()
}
In your custom cell:
class CustomCell: UITableViewCell {
weak var shareDelegate: ShareEventDelegate?
func yourButtonAction() {
shareDelegate.didShareButtonSelected?()
}
}
Then make your ViewControllers conform to the ShareEventDelegate, for example:
extension HomeViewController: ShareEventDelegate {
func didShareButtonSelected() {
// handle your action here
}
}
And in cellForRow:
cell.shareDelegate = self