How to perform the instantiation of UISearchBar properly from a nested VC - swift

I have the firstVC as 'homeController' which has an instance of UISearchBar and implements its delegate (UISearchBarDelegate), and doing this will instantiate the secondVC which is 'searchController' and has its delegate on another instance of UISearchBar as well, I need to fix that redundancy. I am using the homeController UISearchBarDelegate just because it let me instantiate the 'searchController'.
1: I'm using the method for the instantiation searchBarShouldBeginEditing on 'homeController' and perhaps it is wrong because the first second loads the color properly and then it changes to black.
2: searchBarTextDidBeginEditing loop itself. (from the color that I want to again the color that I want). -(infinite loop)
I can only use one at the time or searchBarShouldBeginEditing or searchBarTextDidBeginEditing. I would like to use the behavior of searchBarTextDidBeginEditing, but it reloads like crazy. Or the first second of searchBarShouldBeginEditing and know where does the application is in the state of that black screen because definitely, it is not the default color that I set, because it changes the color that I want on the first second and on the second second it is black. By the way, The application is not using the UISearchBar from the 'searchController', it is using the UISearchBar from 'homeController'. What is recommended?
Just right there I got two points.
1: Should I keep separated the logic as I am doing or should I mix it?
1.1: home and search have different cells, that's why I don't mix them. How can I use the instance of UISearchBar on 'searchController'?
2: If I keep it separated, should I use the UISearchBar from the 'searchController' or delete that instance and just use 'homeController'?
how to implement or instantiate properly the UISearchBar with the current scenario?
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
handleShowSearchVC()
return false
}
#objc func handleShowSearchVC() {
let modalUserSearchController = UserSearchController(collectionViewLayout: UICollectionViewFlowLayout())
modalUserSearchController.modalPresentationStyle = .overCurrentContext
navigationController?.pushViewController(modalUserSearchController, animated: true)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
handleShowSearchVC()
}

Related

How to access an object inside a ViewController from within a TableViewCell

I am fairly new at coding in swift and I've been trying to find a solution to my problem for the past couple days, and am not able to.
I have a class named userData with various properties:
class UserData {
var name: String
var age: Int
var credits: Int = 1
...
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Inside the viewController.swift I have declared an object of this class:
...
var user = UserData(name: "testing", age: 5)
...
Inside the same viewController I have a UITableView with a few UITableViewCells. In every cell there is a UIStepper
Using the UIStepper from a cell I want to increment or decrement the property credits of my object user that sits inside the viewController.swift, and do that from within the tableViewCell.swift file
From what I can find, I think I should use a delegate but I can figure out how to implement it.
More information:
Pardon the art work, I am not an artist...
The user has a set amount of ants available to work (this number is a property of my object user). There is also a property for the amount of ants currently working ('ants used' in my screen shot).
At first, the white label on top says '0/5' (Meaning the user has 5 available ants to work but none are currently working).
When the user increments the stepper for 'scavenger ants', the white label on top should say '1/5' (Meaning that there is currently 1 ant working out of 5 that are available).
What I want to do, is that when the user clicks on a stepper, the user's property for the 'ants currently working' increments or decrements appropriately.
Thank you
Set that logic up in the view controllers' cellForRowAt.
When setting up the cell, you can add a function inside your view controller as the target every-time the UIStepper value changes:
// inside cellForRowAtIndexPath:
cell.addTarget(self, action: #selector(doSomething(sender:), for: .valueChanged)
Inside doSomething you set up the logic for updating your model.
func doSomething(sender: UIStepper) {
// do stuff here
}
Edit with an example of the delegate pattern which would be a better solution, for future readers.
First create a protocol:
protocol StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...)
func otherUsefulFunctions(...)
}
In your cell class, set a target/action for when your value is changed:
// inside the cell's initialization (`init(style:reuseIdentifier:)`
self.addTarget(self, action: #selector(valueChanged(sender:), for: .valueChanged)
Your cell also needs a delegate property:
weak var stepperCellDelegate: StepperCellDelegate?
doSomething would look something like this:
func valueChanged(sender: UIStepper) {
stepperCellDelegate?.didChangeValueForStepper(inCell: self, ...)
}
Your ViewController will implement the protocol
extension MyViewController: StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...) {
// implementation here
}
func otherUsefulFunctions(...){
// implementation here
}
}
And inside cellForRowAtIndexPath set itself as the delegate:
// inside cellForRowAtIndexPath:
cell.stepperCellDelegate = self
Delegation is the proper way of handling this.
That UItableviewCell — both appear to be similar —
should expose a protocol with a set of methods
that the UIViewController can implement. Methods are
triggered when you tap on the UIStepper widget.
Best to stick to this methodology, and reuse it everywhere. Makes
things manageable loosely coupled.

Swift macOS SegmentedControl Action not getting called

Description
I am trying to use NSSegmentedControls to transition between Child ViewControllers. The ParentViewController is located in Main.storyboard and the ChildViewControllers are located in Assistant.storyboard. Each ChildViewController has a SegmentedControl divided into 2 Segments and their primary use is to navigate between the ChildViewControllers. So they are set up as momentaryPushIn rather than selectOne. Each ChildViewController uses a Delegate to communicate with the ParentViewController.
So in the ParentViewController I added the ChildViewControllers as following:
/// The View of the ParentViewController configured as NSVisualEffectView
#IBOutlet var visualEffectView: NSVisualEffectView!
var assistantChilds: [NSViewController] {
get { return [NSViewController]() }
set(newValue) {
for child in newValue { self.addChild(child) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
addAssistantViewControllersToChildrenArray()
}
override func viewWillAppear() {
visualEffectView.addSubview(self.children[0].view)
self.children[0].view.frame = self.view.bounds
}
private func addAssistantViewControllersToChildrenArray() -> Void {
let storyboard = NSStoryboard.init(name: "Assistant", bundle: nil)
let exampleChild = storyboard.instantiateController(withIdentifier: "ExampleChild") as! ExampleChildViewController
let exampleSibling = storyboard.instantiateController(withIdentifier: "ExampleSibling") as! ExampleSiblingViewController
exampleChild.navigationDelegate = self
exampleSibling.navigationDelegate = self
assistantChilds = [exampleChild, exampleSibling]
}
So far so good. The ExampleChildViewController has an NSTextField instance. While I am in the scope of the TextField, I can trigger the action of the SegmentedControls. Its navigating forward and backward as it should. But once I leave the scope of the TextField I can still click the Segments, but they are not triggering any action. They should be able to navigate forward and backward even if the TextField is not the current "First Responder" of the application. I think I am missing something out here, I hope anyone can help me with this. I know the problem is not the NSSegmentedControl because I am seeing the same behavior with an NSButton, which is configured as Switch/Checkbox, in the SiblingViewController. I just don't have any idea anymore what I am doing wrong.
It`s my first time asking a question myself here, so I hope the way I am doing is fine for making progress with the solution. Let me know if I can do something better/different or if I need to provide more information about something.
Thanks in advance!
Additional Information
For the sake of completeness:
The ParentViewController itself is embedded in a ContainerView,
which is owned by the RootViewController. I can't imagine this does
matter in any way, but this way we are not missing something out.
I am actually not showing the navigation action, because I want to
keep it as simple as possible. Furthermore the action is not problem,
it does what I want it to do. Correct me if I am wrong with this.
Possible solutions I found while researching, which did not work for me:
Setting window.delegate of the ChildViewControllers to NSApp.windows.first?.delegate
Setting the ChildViewController to becomeFirstResponder in its func viewWillAppear()
visualEffectView.addSubview(self.children[0].view, positioned: NSWindow.OrderingMode.above, relativeTo: nil)
Related problems/topics I found while researching:
Basic segmented control not working
Adding and Removing Child View Controllers
NSSegmentedControl - Odd appearance when placed in blur view
How to set first responder to NSTextView in Swift?
How to use #selector in Swift 2.2 for the first responder
Accessing methods, actions and/or outlets from other controllers with swift
How to use Child View Controllers in Swift 4.0 programmatically
Container View Controllers
issues with container view
Control a NSTabViewController from parent View
How to detect when NSTextField has the focus or is it`s content selected cocoa
SOLUTION
let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance
In my case I just had to set the delegate as the target of the sendAction method.
Background
Ok, after hours of reading the AppKit Documentation I am now able to answer my own question.
First, debugging the UI showed that the problem was definitely not in the ViewHierarchy.
So I tried to think about the nature of NSButton and NSSegmentedControl. At some point I noticed that both are subclasses of NSControl.
class NSSegmentedControl : NSControl
class NSButton : NSControl
The AppKit Documentation says:
Discussion
Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.
The bold text points to the key of the solution – of its associated target object. Typically I define the action of an control item like this:
button.action = #selector(someFunc(_:))
This causes the NSControl instance to call this:
func sendAction(_ action: Selector?, to target: Any?) -> Bool
Parameter Description from the documentation:
Parameters
theAction
The selector to invoke on the target. If the selector is NULL, no message is sent.
theTarget
The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.
In conclusion the NSControl instance, which was firing the action method (in my case the NSSegmentedControl), had no target to send its action to. So it was only able to send its action method across the responder chain - which obviously has been nil while the first responder was located in another view.

Edit button does not have any functionality, no red circle with a minus appears on the left side of cells

I have looked at another question that was asking the exact same question as mine, but the answer told them to call the setEditing function (which I tried, and say later in the question). But I don't see how you could call this function only when the edit button is clicked. I suppose I could create my own BarButtonItem and run this method when my bar button item is clicked, but I figured this would be far easier since I need the basic functionality of the edit button.
I have a UIViewController that has a table on it named peersTable. When I click the edit button it switches to done, but nothing happens on the table. I have also added my own UITableViewRowActions and when I swipe to the left on the cells, my custom actions do show up.
Here is some of my code:
class PeerViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, ConnectionManagerDelegate {
#IBOutlet weak var peersTable: UITableView!
...
override func viewDidLoad() {
...
peersTable.delegate = self
peersTable.dataSource = self
self.navigationItem.rightBarButtonItem = self.editButtonItem
...
}
I have also tried calling the peersTable.setEditing(true, animated: true) method myself, and in this case, the red minus does appear. I've never seen this issue before, so I don't understand why it's happening. Am I somehow setting the delegate wrong? Or possibly I'm doing something wrong since this is a regular View and not a TableView (even though I've done this before in a previous project).
Thanks in advance! If you need any more information let me know!
Since this is a not a TableViewController it won't automatically set your tableView to editing mode when you press the edit button.
You need to override setEditing method so you can set the tableView to editing mode.
Add this to your ViewController class:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
peersTable.setEditing(editing, animated: animated)
}

Table with static cells won't scroll when I set delaysContentTouches = false

The problem: HIGHLIGHT vs SCROLLING
My buttons inside the cell where not getting highlighted when I lightly tap on them. I had to tap hard and for a long time to be able to see the tap state of the button.
So I did this in order to set the delaysContentTouches to false (I didn't manage other way to do it) inside viewDidLoad():
for index in tableView.subviews {
if (index.isKindOfClass(UIScrollView)) {
let scrollViewFound = index as! UIScrollView
scrollViewFound.delegate = self
scrollViewFound.delaysContentTouches = false
scrollViewFound.canCancelContentTouches = true
scrollViewFound.scrollEnabled = true
}
}
This way the buttons highlight correctly but then I cannot scroll the table up or down, unless I start dragging from one of the empty cells --> userInteractionEnable = false in the empty cells
What I need:
To be able to highlight the buttons but also to scroll the table.
Is it even possible to have both, scrollable view and highlighted buttons?
What I have tried
I tried calling this function:
func touchesShouldCancelInContentView(view: UIView) -> Bool {
print("touchesShouldCancelInContentView happening---------")
return true
}
Which never gets called. I tried overriding But it gives an error:
Method does not override any method from its superclass
Which is weird, because UITableViewController inherits from UIScrollView. I also tried adding UIScrollViewDelegate to the class definition, but of course it gives another error that this is redundant.
Extra Information
The class is declared like this:
class Settings: UITableViewController, UITextFieldDelegate { ...
The tableView is made of Static Cells
The cells:
Some are empty: where UserInteractionEnable = false
Some have buttons with text field: I want these buttons to get highlighted. UserInteractionEnable = true. The button action is called by .TouchUpInside
Some have labels and a check image: Their action gets called in didSelectRowAtIndexPath which will change the labels colour and check images
Maybe it is relevant to say that when user clicks on any cell didSelectRowAtIndexPath it will
call a function to dismiss the keyboard.
You tried to subclass the wrong class, that's why it doesn't work. You have to subclass the UITableView class itself, and not the UITableViewController.
Can you try the following ?
- First
Subclass the TableView class in order to override the touchesShouldCancelInContentView function.
class UIDraggableTableView: UITableView {
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if (view.isKindOfClass(UIButton)) {
return true
}
return super.touchesShouldCancelInContentView(view)
}
}
- Second
In your TableViewController class, when viewDidLoad() is called, append the following right after super.viewDidLoad():
self.tableView = DraggableTableView()
This should solve your issue.
Part of this answer was taken from this StackOverflow post.

why pass viewController as variable?

First of all I have to say I am really new to swift and Objective C.I am learning them by myself.
I have a question for this code
I have a delegate in my SettingViewController called "settingsViewControllerFinished" and it pass the whole controller as a variable.
the code like this:
in my SettingViewController.swift
protocol SettingViewControllerDelegate: class {
func settingsViewControllerFinished(settingsViewController: SettingsViewController)
}
#IBAction func close(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
self.delegate?.settingsViewControllerFinished(self)
}
I am confused.What did you mean if you pass the whole controller as a variable?(maybe the question is silly for you)
in my viewController:
func settingsViewControllerFinished(settingsViewController: SettingsViewController)
{
self.brushWidth = settingsViewController.brush
self.opacity = settingsViewController.opacity
self.red = settingsViewController.red
self.green = settingsViewController.green
self.blue = settingsViewController.blue
}
I guess the reason is:I pass everything in SettingViewController to ViewController so that I could use the variables in SettingViewController.
Am I rihgt?
Generally you are correct, yes: passing the SettingViewController back to its delegate enables the original caller to not have to keep a reference to the created and shown SettingViewController since the delegate method sends the relevant information along already.
But there is more: In some cases of delegates this style is useful for something different. Imagine a click handler consisting of a function func somethingGotClicked(sender:YourSenderType). If your class creates multiple instances of YourSenderType and shows them at the same time registering itself as their delegate there would be no way to know which one got clicked if there was no sender parameter. In some func somethingGotClicked() you would not know which one got clicked. That capability is often needed when showing multiple UITableView or UICollectionView is one single view with one single instances set as their delegate.