Can't link Refresh Control to IBAction in Swift - ios

I am trying to get a Refresh Control to link to an IBAction function in my View Controller. When I ctrl-drag it only gives me the option to create an Outlet.
I added the Refresh Control simply by setting Refreshing to Enabled in the Attributes Inspector.
Everything else is set up properly as far as I'm aware, the View Controller is set in the Identity inspector and I'm attempting to link to the correct Class.
Any Advice

I know it's not your solution of your problem.But you can create Refresh Control programmatically.Like...
var refreshCntrl : UIRefreshControl!
refreshCntrl = UIRefreshControl()
refreshCntrl.tintColor = UIColor.whiteColor()
refreshCntrl.attributedTitle = NSAttributedString(string: "Loading...")
refreshCntrl.addTarget(self, action:("refreshControlValueChanged"), forControlEvents: UIControlEvents.ValueChanged)
atableView.addSubview(refreshCntrl)
//Method of RefreshControl
func refreshControlValueChanged(){
atableView.reloadData()
refreshCntrl.endRefreshing()
}

Happened to me too, it gives you an Outlet option because you didn't select the button as you thought but the bar. Hence, for letting it set an Action you need to higlight the Bar Button in your controller's top-down menu on the left column.
Hope it helps!

Related

Setting UIView.isAccessibleElement to true disables clicking of subview in voiceovermode

So I have a custom view controller that displays a dialog with a couple of buttons.
When the view appears I want voiceover to read out some basic information describing the dialog.
To achieve this I made the parent view to be an accessible element and the subviews which are two buttons are also accessible elements.
My problems now is that the buttons are not clickable directly.
They must be reached only by swiping right on the screen.
class MyViewController: UIViewController {
let parent = UIView()
let button1 = UIButton()
let button2 = UIButton()
init() {
parent.addSubview(button1)
parent.addSubview(button2)
parent.isAccessibilityElement = true
button1.isAccessibilityElement = true
button2.isAccessibilityElement = true
parent.accessibilityLabel = "Message"
self.view.addSubview(parent)
self.view.accessibilityElements = [parent, button1, button2]
}
override func viewDidAppear(_ animated: Bool) {
}
}
If there is a better way to get voiceover to give description of the view when the opens, I am open to that too.
Also, the view needs to be a modal so that focus is trapped on the view.
To achieve this I made the parent view to be an accessible element and the subviews which are two buttons are also accessible elements.
That's definitely the problem: you can't have the parent view and its children accessible all together ⟹ see the example sheet of this explanation.
If a parent view is accessible, its children won't be seen by VoiceOver and conversely.
If there is a better way to get voiceover to give description of the view when the opens, I am open to that too.
Using VoiceOver, you must be as accurate and brief as possible.
The description of a view is provided by its elements when you explore the screen or by its title itself : in my view, you shouldn't read out a description that a perfect title should provide in addition to the correct implementation of the different components of your page.
There's a great presentation made by a blind person who explains how to write labels inside an app to be well understood.
Also, the view needs to be a modal so that focus is trapped on the view.
The best way to reach this purpose is to use the accessibilityViewIsModal property of your view ⟹ take a look at this example introduced during a WWDC session if need be.
You can post a notification with a message as parameter, so you would not need to set the parent view as an accessibility element. This would solve both of your problems.
Example code:
let parentVc = UIView()
let button1 = UIButton()
let button2 = UIButton()
init() {
parentVc.addSubview(button1)
parentVc.addSubview(button2)
button1.setTitle("btn1", for: .normal)
button2.setTitle("btn2", for: .normal)
button1.isAccessibilityElement = true
button2.isAccessibilityElement = true
self.view.addSubview(parentVc)
self.view.accessibilityElements = [button1, button2]
UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: "Message here");
}

button action with multiple parameter

I'm working with swift. I've this view controller:
In this picture from 1.1.1 to 1.2.6 all are uibutton. This button r created programatically. Now on each button click I need to pass 2 parameter. If I consider 1.1.1 Overflater , then I need to pass "1.1.1" and "Overflater" as parameter. I've tried like below:-
ov_button.tag = "1.\(i).1"
ov_button.addTarget(self, action: #selector("i3Vatrom.romBeskrivelse:")
, forControlEvents: UIControlEvents.TouchUpInside)
But it's not working. Any idea how to do this....
I think, you should use UITableView hierarchy if you don't used UITableView, use UITableView and use section and row tag properties for managing your buttons. didSelectRowAtIndexPath function using for button clicks.
You cannot pass anything to an action handler function. The action handler function automatically receives the sender (here, the button) as a parameter — and that's all. The trick, therefore, is to build the knowledge of the information you want passed into the button object itself, since this will be what is passed! In your case, it looks like you could just ask for the button's title for the Normal state and parse it into the "1.1.1" and "Overflater".
after trying and thinking so much I found an easy solution of this question.
below code for button add target:-
ov_button.addTarget(self, action: #selector(i3Vatrom.romBeskrivelse), forControlEvents: UIControlEvents.TouchUpInside)
I've used below function:-
func romBeskrivelse(sender:UIButton!) {
let title = sender.titleLabel!.text!
let titleArr = title.characters.split{$0 == " "}.map(String.init)
print(titleArr) // print ["1.2.1", "Overflater"]
}
That's why I thought I must post the answer. may be this will help other.

UIButton never fires off code in Action

I have the following screen:
The X is the image of a UIButton, I have add the appropriate action to the button. Yet when I click on the button it never fires off the code in the action.
Here is some code:
#IBAction func CloseProfilePage(sender: AnyObject) {
self.removeAnimate();
}
This is the code that is used to launch the view controller seen:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectedAppointment = self.dayDatasource!.appointmentAtIndex(indexPath.item);
let profilePageViewController = ProfilePageViewController.init(withAppointment: selectedAppointment);
profilePageViewController.view.frame = self.view.frame
self.view.addSubview(profilePageViewController.view)
profilePageViewController.didMoveToParentViewController(self)
}
The button is definitely connected in the xib to the action:
When I check in the view hierachy, there isn't anything else on top of the button that would prevent the button but registering clicks. I'm assuming here that the imageView in the UIButton is clickable as its part of the button iteself.
The X in the image is not an image view I added, it is the image view that comes with the UIButton. With that said I've also resorted to the following:
self.profilePageClosePopUpButton.imageView?.userInteractionEnabled = true;
Still the button remains unclickable. Where am I going wrong?
It pains me to say this but I'm only writing a solution here just in case someone in the future struggles with the same issue and maybe this post could help them.
In the view, seen in the image below, I had some how unintentionally switched off User Interaction Enabled in interface builder. Because this was off, all other children didn't have interaction enabled on them and hence why the button was not clickable...
Moral of the story is, check your parent views and make sure their user interaction is enabled as well.

How to connect UIButton to action using only interface builder

At the moment I am setting up my buttons for my keyboard with the following code:
func setupButtons() {
for subview in self.view.subviews {
if subview.isKindOfClass(UIButton) {
setupButton(subview as! UIButton)
}
}
}
func setupButton(btn: UIButton) {
btn.addTarget(self, action: #selector(KeyboardViewController.keyPressed(_:)), forControlEvents: .TouchUpInside)
}
But is there anyway I can skip this and all the target inside the layout builder so I can save a little bit of time looping through buttons on each keyboard view?
Sure, there are two ways to connect objects to Interface Builder, through outlets and actions.
class ViewController: UIViewController {
#IBOutlet weak var doStuffButton: UIButton!
#IBAction func doStuff(sender: UIButton) {
//do stuff
}
}
After adding that code look in interface builder and click on the view controller for this class.
Click on the connections inspector
Under outlets you will now see doStuffButton, however over the circle to the right of that, press control and click with your mouse, and drag it over to the button you want to connect it to and release. Setting outlets is NOT required for enabling actions. I just wanted to show this to you as well.
In the same pane you will also see received actions and the doStuff: method. Click and drag in the same way and release on the button. Select which type of event you want to process this action (normal is touch up inside).
Once you're all hooked up it should look like this:
There are many other variations of how to do this, but I thought this would get you a quick start.
If you're trying to ask how to do this without coding anything, just go into the assistant editor view of Xcode and Ctrl-drag from your button to the controllers class file. Then when the pop up displays, change outlet to action and give it a method name. This creates the IBAction method for you.
But in reality, the way you are doing it now with the for loop is far better. Especially if you have many buttons.

UITableView and UIRefreshControl being moved down for unknown reason

I have a UITableViewController in my app with a UIRefreshControl added to it. Sometimes however (I'm not sure how to reproduce this, it happens every now and then), I get some extra whitespace at the top of the table view with the refresh control being offset even below that.
This is what it looks like (idle on the left, being pulled down on the right):
I don't have any clue what could be causing this. In my viewdidload I'm only instantiating the refresh control and calling an update function that sets the attributed title. I've moved adding the refresh control to the table view into the viewDidAppear as I've read elsewhere. This is what that code looks like:
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
updateData()
}
override func viewDidAppear(animated: Bool) {
refreshControl!.addTarget(self, action: "updateData", forControlEvents: UIControlEvents.ValueChanged)
tableView.insertSubview(self.refreshControl!, atIndex: 0)
}
func updateData() {
//...
ServerController.sendParkinglotDataRequest() {
(sections, plotList, updateError) in
//...
// Reload the tableView on the main thread, otherwise it will only update once the user interacts with it
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
// Update the displayed "Last update: " time in the UIRefreshControl
let formatter = NSDateFormatter()
formatter.dateFormat = "dd.MM. HH:mm"
let updateString = NSLocalizedString("LAST_UPDATE", comment: "Last update:")
let title = "\(updateString) \(formatter.stringFromDate(NSDate()))"
let attributedTitle = NSAttributedString(string: title, attributes: nil)
self.refreshControl!.attributedTitle = attributedTitle
})
}
}
Do you need to add the refresh control as a subview of the tableView? I think all you need to do is assign self.refreshControl. According to the documentation:
The default value of this property is nil.
Assigning a refresh control to this property adds the control to the
view controller’s associated interface. You do not need to set the
frame of the refresh control before associating it with the view
controller. The view controller updates the control’s height and width
and sets its position appropriately.
Adding a subview in viewDidAppear could get executed more than once. If you push a controller from a cell and pop back this will get called again. It could be that insertSubview checks if the refresh already has a parent and removes it first, so might not be your issue. You should only do the insert when the controller appears for the first time.
updateData could also be getting added multiple times.
So I think you only need to assign self.refreshControl and then add a handler for the refresh action as you do now using addTarget but this time do it on self.refreshControl.
You can also do all this from storyboard. In storyboard you select the UITableViewController and on the attribute inspector simply set the Refreshing attribute to enabled. This adds a UIRefreshControl into the table and you can see it on the view hierarchy. You can then simply CTRL drag as normal from the refresh control into the .h file and add an action for valueChange which will be fired when you pull down on the refresh control in the table.
Well, I believe that your described behavior might not necessarily be caused by the refresh control.
According to the fact that you don't have any other subviews below your table view I would recommend you to try to place a "fake"-view below your table view. I usually prefer an empty label with 0 side length.
I had similar issues like yours where my table view insets were broken in some cases. And as soon as I used this "fake" subview the problems disappeared. I've read about this issue in some other threads, too. And the solution was this. Seems to be an odd behavior/bug.
Give it a try :)

Resources