I want to create a function that adds a refreshControll to a UITableView.
The problem I have is the #selector:
extension UITableView{
func refreshTableView(callFunction: #selector()){
refreshControl = UIRefreshControl()
refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl?.addTarget(self, action: #selector(callFunction), for: .valueChanged)
self.addSubview(refreshControl!)
}
}
I want to specify which function to call when I use this function.
Is this even possible?
#selector(...) is an expression of type Selector, so your method should accept a Selector object and pass it directly to the action: parameter.
extension UITableView {
func refreshTableView(callFunction: Selector) {
// ...
refreshControl?.addTarget(self, action: callFunction, for: .valueChanged)
// ...
}
}
myTableView.refreshTableView(callFunction: #selector(whatever))
Related
I'd like to have a protocol:
protocol CameraButtonDelegate: class {
func cameraButtonDidPress(_ sender: UIButton)
}
So, I could assign any client to a button, e.g.:
cameraButton.addTarget(delegate, action: #selector(cameraButtonDidPress), for: .touchUpInside)
However, it does not compile as I have to specify a particular function in action, e.g.:
cameraButton.addTarget(delegate, action: #selector(AAPLViewController.cameraButtonDidPress), for: .touchUpInside)
How to solve this issue, if I'd like to have multiple clients being targeted by a single button?
It should work if you make the protocol #objc
#objc protocol CameraButtonDelegate: class {
func cameraButtonDidPress(_ sender: UIButton
}
and specify the selector as protocol method
cameraButtonDidPress.addTarget(delegate, action: #selector(CameraButtonDelegate.cameraButtonDidPress), for: .touchUpInside)
You can try
cameraButton.addTarget(Service.shared, action: #selector(Service.shared.cameraButtonDidPress(_:)), for: .touchUpInside)
//
class Service {
static let shared = Service()
#objc func cameraButtonDidPress (_ sender:UIButton){
}
}
The following code is located inside a subclass of UIView
I am setting up a cancelButton inside a closure:
private var cancelButtonClosure: UIButton = {
...
button.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
}()
And at first I instantiated the button inside a function like so:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
...
addSubview(cancelButton)
...
}
However this resulted in the cancel function not being called at all (even though the layout was right and the button was highlighting)
So I made these change:
Removed the addTarget part from the cancelButtonClosure
Added the addTarget part inside the showConfirmationView function
So it looked like that:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
cancelButton.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
addSubview(cancelButton)
...
}
It worked: the cancel function was called; but I don't know why. I'm really curious to know why what I did before did not work. Thanks for your insights!
Check your implementation because a setup like this works as expected:
private var cancelButton: UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Cancel", for: .normal)
btn.addTarget(self, action: #selector(cancelSomething(_:)), for: .touchUpInside)
return btn
}()
#objc func cancelSomething(_ sender: UIButton) {
print("Something has to be cancelled")
}
override func viewDidLoad() {
super.viewDidLoad()
showConfirmationView()
}
func showConfirmationView() {
cancelButton.sizeToFit()
cancelButton.center = view.center
view.addSubview(cancelButton)
}
I have a setup that brings up a date picker upon clicking of a button. I have two a function that changes the value of a button upon selecting a date. In addition, I also have a function that should help dismiss the date picker when I press the done button. However, I continue to get an error stating 'unrecognized selector sent to instance'. Any idea what I could have possibly done wrong
#IBOutlet weak var dueDateSelector: UIButton!
#IBOutlet weak var myPickerView: UIPickerView!
#IBAction func changeDueDate(_ sender: Any) {
//Create the view
let inputView = UIView(frame: CGRect(x:0, y:420, width: self.view.frame.width, height: 240))
var datePickerView : UIDatePicker = UIDatePicker(frame: CGRect(x:0, y:40, width:0, height:0))
datePickerView.datePickerMode = UIDatePickerMode.date
inputView.addSubview(datePickerView) // add date picker to UIView
let doneButton = UIButton(frame: CGRect(x:270, y:0, width:100, height:50))
doneButton.setTitle("Done", for: UIControlState.normal)
doneButton.setTitle("Done", for: UIControlState.highlighted)
doneButton.setTitleColor(UIColor.black, for: UIControlState.normal)
doneButton.setTitleColor(UIColor.gray, for: UIControlState.highlighted)
inputView.addSubview(doneButton) // add Button to UIView
doneButton.addTarget(self, action: "doneButton:", for: UIControlEvents.touchUpInside) // set button click event
datePickerView.addTarget(self, action: Selector("handleDatePicker:"), for: UIControlEvents.valueChanged)
view.addSubview(inputView)
handleDatePicker(sender: datePickerView) // Set the date on start.
}
#objc func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dueDateSelector.setTitle(dateFormatter.string(from: sender.date), for: .normal)
}
#objc func doneButton(sender:UIButton)
{
dueDateSelector.resignFirstResponder() // To resign the inputView on clicking done.
}
This is due to an inconsistency in naming methods between Swift and Objective C. This is also why you should write selectors with the #selector syntax instead of using strings. You need to change this line:
datePickerView.addTarget(self, action: Selector("handleDatePicker:"), for: UIControlEvents.valueChanged)
Selectors are an Objective C thing. If you write it in strings, then you need to write it in an Objective C fashion. In your code, handleDatePicker is not actually called handleDatePicker: in Objective C. It is probably something like handleDatePickerWithSender:. This is because you have a sender argument there, and that becomes the WithSender part in the eyes of Objective C.
This is why we like to write selectors like this:
#selector(handleDatePicker)
No need to worry about parameters, just the method name.
datePickerView.addTarget(self, action: #selector(handleDatePicker), for: UIControlEvents.valueChanged)
Also, this line needs to be changed as well:
doneButton.addTarget(self, action: "doneButton:", for: UIControlEvents.touchUpInside)
It should be:
doneButton.addTarget(self, action: #selector(doneButton), for: UIControlEvents.touchUpInside)
I think you are having problem here,
#objc func doneButton(sender:UIButton)
{
dueDateSelector.resignFirstResponder() // To resign the inputView on clicking done.
}
here dueDateSelector is button which is not having any method called resignFirstResponder() so that's why you are getting error
instead you can do this,
#objc func doneButton(sender:UIButton)
{
//remove or hide the input view
}
You can also check that whether current component having this method or not with,
if dueDateSelector.canResignFirstResponder()
{
}
You are using deprecated string selector syntax:
doneButton.addTarget(self, action: "doneButton:", for: UIControlEvents.touchUpInside)
Should be replaced with:
doneButton.addTarget(self, action: #selector(doneButton(sender:)),
for: .touchUpInside)
It's also applied to the other method in your code:
datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)),
for: .valueChanged)
doneButton.addTarget(self, action:#selector(self.doneButton(_:)) , for:
UIControlEvents.touchUpInside) // set button click event
datePickerView.addTarget(self, action: #selector(self.handleDatePicker(_:)), for: UIControlEvents.valueChanged)
if self wont work use class reference like yourClass().doneButton
I have subclassed UIButton and want to call a delegate method just once when the button goes into the highlighted state and call it again just once when it goes into the unhighlighted state:
override var isHighlighted: Bool {
didSet {
if isHighlighted {
delegate?.buttonHighlightStateDidChange(highlighted: true)
} else {
delegate?.buttonHighlightStateDidChange(highlighted: false)
}
}
}
However when I touch down on the button it seems that didSet is getting repeatedly called. What am I doing wrong here? How can I call the delegate method just once?
I would recommend against using your subclass in this way. UIControl has a builtin mechanism for getting callbacks in response to control events:
func registerActions(for button: UIButton) {
button.addTarget(self, action: #selector(MyClass.buttonIsHighlighted(sender:)), for: .touchDown)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpInside)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpOutside)
}
func buttonIsHighlighted(sender: UIButton) {
// highlighted
}
func buttonIsUnHighlighted(sender: UIButton) {
// unhighlighted
}
I'm trying to implement pull to refresh in my table view app. I've been looking around at people's examples and I've gathered that this is pretty much the gist of it:
var refreshControl:UIRefreshControl!
override func viewDidLoad()
{
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(refreshControl)
}
func refresh(sender:AnyObject)
{
// Code to refresh table view
}
However the only examples I can find are from a while ago and I know the language has changed a lot since then! When I try to use the code above, I get the following error next to my declaration of refreshControl:
Cannot override with a stored property 'refresh control'
My first thought before reading other examples is that I would have to declare the variable like so:
var refreshControl:UIRefreshControl = UIRefreshControl()
Like I did with some other variables but I guess not.
Any ideas what the issue is?
I gather your class inherits UITableViewController. UITableViewController already declares the refreshControl property like this:
#availability(iOS, introduced=6.0)
var refreshControl: UIRefreshControl?
You don't need to override it. Just get rid of your var declaration and assign to the inherited property.
Since the inherited property is Optional, you need to use a ? or ! to unwrap it:
refreshControl = UIRefreshControl()
refreshControl!.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl!.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl!)
Just add this code in viewDidLoad
self.refreshControl = UIRefreshControl()
self.refreshControl!.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl!.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(refreshControl!)
Works Fine :)