Access to Checkboxes (NSButton) in a Controller View - swift

I have a View with 50 checkboxes (NSButton) in a OS X app. I need to access to each one in code, but the only way I find is to create 50 IBOutlet bindings. The IBOutlet collection is not available in OS X. How can I do?

One way to refer to the 50 checkboxes in your ViewController class (I'm assuming that you use view controllers) is by first retrieving the superview that contains each of the 50 checkboxes. This will be an NSView object. To distinguish between the checkboxes I recommend setting different tag values right in interface builder, but it really depends on what you want to do with the checkboxes. Then, you can use a loop to iterate through each of the subviews in this view, like this:
for i in view.subviews.filter({$0 as? NSButton != nil}).map({$0 as! NSButton}) {
if i.bezelStyle == .regularSquare && !(i.cell as! NSButtonCell).imageDimsWhenDisabled {
print(i.title)
}
}
The if condition basically utilizes two properties of checkboxes to distinguish them from any other type of NSButton.
In this example, I simply printed the title of each checkbox. You can use a switch statement if you want to perform different tasks depending on which checkbox it is. The good thing is that with this method you can have infinitely many checkboxes. The iteration order is left to right, top to bottom.

Related

Why can't I send data between interface controllers for Apple Watch?

Basically, what I'm trying to do is pass an integer from one interface controller. The slider in the controller "SecondPage" will get an integer value from the slider and should send it to the interface controller called "ThirdPage" and set the title of that label to the value. I've tried so many methods but all to no avail. I'm just trying to send the context from the 2nd interface controller, receive it in the awake(withContext context: Any?) method in the 3rd controller and save it as the title until the slider is changed again.
Here's what I tried first in the second page controller, but it didn't pass any data:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
if segueIdentifier == "ThirdPage"{
return sliderval
}
return nil
}
here's what I tried after that, it passed the data but it doesn't "save" it. It pops up the interface controller with the updated label to the slider value, but I don't want it to pop up, I want that value to be saved to the label. When I close the pop up, and swipe right to the Third Page, the label is still called "Label"
self.presentController(withName: "ThirdPage", context: sliderval)
Here's some pictures of the output as well as some of my code. Any help is appreciated, thank you. PS: This is all in Swift 3
You are using a page-based navigation, and you need to pass data between pages.
What I would do is save the data from the slider somewhere that the third page can access, and populate the data in the label on willActivate. The willActivate method should be called by the system when the user swipes between pages.
At the global scope, create a variable to hold the slider value.
var sliderValue: Float = 0
In your second interface controller, set this value when the slider changes.
#IBAction func sliderChanged(_ value: Float) {
sliderValue = value
}
In your third interface controller, when the page is about to appear, set the label to the correct value.
override func willActivate() {
label.setText("\(sliderValue)")
}
If you want to avoid the use of global variables you can read and write from user defaults, or use other tactics, but this will depend on the exact needs of your application.
Your undesired popup problem is happening because you're calling presentController() in sliderChanged(). Every time the slider changes, you're telling it to pop up the "ThirdPage" controller. You can just delete that.
Edit: My answer incorrectly assumed the asker is working with hierarchical navigation. For page-based navigation, there is apparently no way to pass data from one page to the next, so you're stuck with using a global as the accepted answer suggests. The original answer follows:
As for the lack of passing context, I would guess you probably don't have the segue identifier set in Interface Builder, or it's set to something other than "ThirdPage". The segue is separate from the "ThirdPage" controller, and you have to set its identifier separately. Click on the circle in the middle of the segue arrow and take a look at it in the Attributes inspector.
Assuming I'm right about that, the problem is that contextForSegueWithIdentifier() is being called with an identifier that you don't expect. Since it's not "ThirdPage", the conditional fails and it returns nil. Setting the segue identifier should fix it.

UIPickerView and UI Unit Test: How to get values of UIPickerWheel?

Recently I had a bad headache (and I'm still struggling) to find out how to retrieve all values inside an UIPickerWheel. For me should be enough to move at particular row of the wheel, but I can't! So frustrating! I tried to scroll row by row to retrieve all values (https://stackoverflow.com/a/39300344/821407) but it's so slow! Any clue?
NB: I can't use adjustToPickerWheelValue because my root problem is that I don't know the value since they are dynamic and I would like to avoid launchArguments/launchEnvironment.
This is probably not the answer you were hoping for but it is not possible to get the title of all rows in a UIPickerView in a UITest.
As you know when running a UITest you can only access your app's UI elements via the XCUIElement class. That class has a value property that gives you some information about the UI element you access. When accessing a UIPickerView the value gives you the title of the currently selected row. But only of the selected row. You can access the picker's row elements, but unfortunately the value property for the row elements is always empty. So, no luck here. All you the info you can get is the number of rows of your picker.
This is not really surprising though. Even if you had access to the UIPickerView, you could not access the titles of all rows directly. UIPickerView does not know about the titles that it displays. It is the job of the UIPickerViewDataSource to provide the titles.
So, unfortunately, if you need to know all the row titles of your UIPickerView in a UITest, you really have to select each value one by one via your app's user interface.
But it does not have to be as complicated as in the answer you linked. Instead of simulating a scroll you can simply tap on the next row to select it (should be slightly faster):
let pickerView = app.pickerWheels.element
let numRows = pickerView.children(matching: .any).count
var values: [String] = [pickerView.value as! String]
for _ in 0..<numRows {
pickerView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.55)).tap()
values.append(pickerView.value as! String)
}
In your question you do not really describe what you are trying to test. But if you want to test if the picker has the correct row titles maybe a UnitTest would be a more practical approach?

UITableViewController- showing only rows with content and an image where there is none

When creating a UITableViewController there are two situations that create an "ugly" UX
calling/open it without any data in it --> shows an empty table (i.e. empty rows,UITableViewCell, as many as fit in the window)
calling/open it with fewer rows of content that fit the window --> show the full rows followed by empty rows
I wish to receive the following result:
if there is no data show a picture or view with text - there isn't any data yet or something like that
show only the full lines and no more rows (blank or background image)
Is there a way to achieve that?
To add these effects, you will probably have to make your own UITableViewController from a regular UIViewController, and even subclass UITableView. If you have a regular UIViewController with your filler image/text as the background, you can place a UITableView on top and hook up the delegate/datasource. Now, in your code, detect when there is no data available and set the hidden property of the UITableView accordingly.
As for the following empty rows, you will either have to turn off the row separators (in IB), or subclass a UITableView (can't help you there). Good luck!

Passing data from one view controller to another without using Segue

I have a UITableView (let’s say class “Parent” which is in view "screen 1"). When I click on one of the Parent’s cell's, I’m displaying another UITableView (let’s call it class “child” in view “Screen2”). Parent and child are connected through “segue” and I’m able to pass the data using “segue”.
For example, parent is having cell – “Cell1” and on touch “Cell1”, I’m getting “Cell11”,”Cell12”,”Cell13” [this is “screen2, table view object”].
Now, there are some descriptions associated with “Cell11” (Once I touched “Cell11”) and i'd like it to display Cell11’s description in another view controller (“screen3”). Here, to pass information between “screen2” to “screen3” I'd rather not to use a “Segue”.
How can I do this?
There are two ways in which you can present a new screen.
1) with the interface builder, here you use a segue to pass something onto the new screen.
2) manually instantiating it, after it is instantiated you can just present it.
Considering you do not want to use method 1, you can use method two like this:
ScreenBClass *screenB = [self.storyboard instantiateViewControllerWithIdentifier:#"screenB"];
screenB.objectPointerInOtherClass = self.objectIWantToPass;
[self screenB animated:YES];

Drawing the UIPicker values from multiple components?

I have the UIPicker setup with multiple components and a button below it. Depending on what the user has chosen with the UIPicker determines which new view will be loaded but I am having trouble determining how to extrapolate the information from the picker itself. Right now I have this method being called when the button is pressed:
- (IBAction) buttonPressed {
if (component:1 == 1 && component:2 == 1) {
//Load the view number 1.
} else if (component:1 == 2 && component:2 == 1) {
//Load the view number 2.
} else {
//Load the view number 3.
}
}
I obviously know that my code is wrong but I hope it gets the point across. I have multiple components and I need to figure out how to use the information that the user is scrolling to on the picker to determine which view to move to. (I know how to load the views, I just commented those in the code to illuminate the problem areas better.)
How do I go about using the pickerViews to extrapolate the information IN the buttonPressed IBAction method?
To respond directly to picker manipulation, use the delegate's pickerView:didSelectRow:inComponent:.
To obtain the view corresponding to user selection, use viewForRow:forComponent:. From that view you can obtain information about what it displays (if it's a UILabel, inspect its text property).
Also, you can obtain this information directly from the data source, referring to the piece of information you would return in the delegate's pickerView:titleForRow:forComponent: or pickerView:viewForRow:forComponent:reusingView:, for the currently selected row and component.