I have a UISegmentedControl in a UITableView Table row. The selection fires, and the new segment is highlighted. The problem is, the old segment remains selected.
If I close and reopen the popover that contains the table, the correct segment index is displayed.
I'm running XCode 6.1 and testing on iOS 7.1 simulator.
Assistance appreciated.
UITableViewCell *segmentCell = [tableView dequeueReusableCellWithIdentifier:segmentCellIdentifier];
if (segmentCell == nil) {
segmentCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:segmentCellIdentifier];
}
segmentCell.backgroundColor = [UIColor clearColor];
segmentCell.backgroundView = nil;
NSArray *segmentItems = [NSArray arrayWithObjects: NSLocalizedString(#"settingsBackgroundCork", #"Cork - Select cork background theme"), NSLocalizedString(#"settingsBackgroundDark", #"Dark - Select dark background theme"), NSLocalizedString(#"settingsBackgroundLight", #"Light - Select light background theme"), nil];
self.backgroundSegmentedControl = [[UISegmentedControl alloc] initWithItems: segmentItems];
[self.backgroundSegmentedControl addTarget:self action:#selector(backgroundControlChanged:) forControlEvents: UIControlEventValueChanged];
self.backgroundSegmentedControl.frame = CGRectMake(10, 6, 300, 32);
NSInteger background = [[NSUserDefaults standardUserDefaults] integerForKey:kUserDefaultsBackgroundSelection];
self.backgroundSegmentedControl.selectedSegmentIndex = background;
self.backgroundSegmentedControl.momentary = NO;
[segmentCell.contentView addSubview:self.backgroundSegmentedControl];
cellToReturn = segmentCell;
Here is the method that gets called on segment selection:
- (void)backgroundControlChanged:(UISegmentedControl *)control
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:control.selectedSegmentIndex forKey:kUserDefaultsBackgroundSelection];
self.backgroundSegmentedControl.selectedSegmentIndex = control.selectedSegmentIndex;
[self.backgroundSegmentedControl setNeedsDisplay];
[[NSNotificationCenter defaultCenter] postNotificationName:kPageBackgroundShouldChangeNotification object:nil];
}
Strange bug - I never saw this before. Two suggestions:
Try setting the selectedSegmentIndex again in the handler, and
Try calling setNeedsDisplay on the control there as well.
momentary is NO by default, so you should not need to set it.
Also, in the selectedSegmentIndex documentation it says:
The default value is UISegmentedControlNoSegment (no segment selected) until the user touches a segment. Set this property to -1 to turn off the current selection.
Maybe you want to try this last thing as well.
I was able to fix this issue by moving my call to setSelectedSegmentIndex to inside the setNeedsLayout function, instead of calling it right after creation of the UISegmentedControl. Of course I also had to create a variable to keep track of which segment should be selected.
Had the same issue. Resolved it by removing the previous instance of the segmentedControl from the superview before adding the new instance.
Adding the below-mentioned 3 lines of code to remove previous segmentedControl before adding the new instance of segmentedControl to the parent view.
self.backgroundSegmentedControl.tag = 101
if let foundView2 = segmentCell.contentView.viewWithTag(101) {
foundView2.removeFromSuperview()
}
[segmentCell.contentView addSubview:self.backgroundSegmentedControl];
Related
I am facing an issue that my segmented controller is not saving its position after closing the application and opening it again.
My code is as per below:
- (void)viewDidLoad {
[super viewDidLoad];
[self.segmentedControlButtonStyle addTarget:self action:#selector(changeButtonStyle:) forControlEvents:UIControlEventValueChanged];
}
- (IBAction)changeButtonStyle:(id)sender {
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.number.application"];
NSInteger selectedSegmentedControlerIndex = self.segmentedControlButtonStyle.selectedSegmentIndex;
if (sharedDefaults) {
[sharedDefaults setInteger: selectedSegmentedControlerIndex forKey:#"MySelectedButtonStyleKey"];
[sharedDefaults synchronize];
}
}
The funny thing is that NSUserDefaults actually is saving correct index because from method I provided above if I change button style it will keep changed after closing and opening application again because I can see it by fact but segmented controller itself is not showing correct segment.
I am not sure why this is happening because I am synchronizing after each segment change but still segmented controller keeps its default position.
in view did load you should add code to set your saved segment
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.number.application"];
int mySegment = [sharedDefaults integerForKey:#"MySelectedButtonStyleKey"];
if(mySegment) {
self.segmentedControlButtonStyle.selectedSegmentIndex = mySegment;
}
I have seen this for a normal UIButton:
NSArray *actions = [viewController.addButton actionsForTarget:viewController forControlEvent:UIControlEventTouchUpInside];
XCTAssertTrue([actions containsObject:#”addNumbers:”], #””);
But now I want to do same thing for a rightBarButtonItem! I have tested this button exist on the VC but there is no interface actionForTargets!
I tried this also but it did not work:
NSArray *actions = [self.navigationItem.rightBarButtonItem actionsForTarget:self forControlEvent:UIControlEventTouchUpInside];
or
NSArray *actions = [[self.navigationItem.rightBarButtonItem target] actionsForTarget:self forControlEvent:UIControlEventTouchUpInside];
Non of them works. Anyone has written test code for a UIBarButton to check if button is connected to correct IBAction?
You can loop through the subviews of the navigationBar, and find a UIControl whole allTargets method contains the rightBarButtonItem.
Once you have that, you can call actionForTarget:forControlEvents: on that UIControl, where the rightBarButtonItem is the target, and UIControl's allControlEvents is the forControlEvents parameter:
//get the underlying control
for(UIControl * control in self.navigationController.navigationBar.subviews)
{
if([control isKindOfClass:UIControl.class])
{
//we found the right one
if([control.allTargets containsObject:self.navigationItem.rightBarButtonItem])
{
NSArray <NSString*> * actions = [control actionsForTarget:self.navigationItem.rightBarButtonItem forControlEvent:[control allControlEvents]];
//do something with actions.firstObject;
return;
}
}
}
However, I believe the subviews of the UINavigationBar has no publicly defined layout, so this is a fragile solution.
In iOS 9, I see a new window appearing in my app that I didn't see before. An image is below. From walking the view tree, I suspect it may be coming from the UIRemoteKeyboardWindow -- but I don't know that. What is it, and what do I have to do to keep it from appearing?
EDIT: As a commenter pointed out, this is tied to the inputView, i.e. the keyboard. I don't want a keyboard and so disabled it by calling
self.inputView = [[UIView alloc]initWithFrame: CGRectZero];
That did kill the keyboard, but there is still the accessory. I've tried similar tricks to kill the accessory; none of them have worked, yet. Calling self.inputAccessoryView is returning nil, which doesn't help.
This got rid of it:
-(void) killAccessory {
UIView* input = self.inputView;
UIView* parent = input.superview;
parent.hidden = YES;
}
-(BOOL) becomeFirstResponder {
BOOL r = [super becomeFirstResponder];
[self killAccessory];
return r;
}
You can use the following code :
textField.inputAssistantItem.leadingBarButtonGroups = [[NSArray alloc] init];
textField.inputAssistantItem.trailingBarButtonGroups = [[NSArray alloc] init];
the textField use above is the textField which you tap to edit.
I have a TableView that contains a list of names for the cell text taken from an NSArray called arryData. In the didSelectRowAtIndexPath, I have:
NextViewController *nextController = [[NextViewController alloc] initWithNibName:#"NextViewController" bundle:nil];
[self.navigationController pushViewController:nextController animated:YES];
[nextController changeProductText:[arryData objectAtIndex:indexPath.row]];
In the NextViewController I have:
- (IBAction) changeProductText:(NSString *)str{
NSLog(#"whosthere%#", str);
self.title = str;
if ( [str isEqualToString: #"Row 1 Name"]) {
lblProductTxt.image = [UIImage imageNamed:#"row1name.png"];
job.text = #"Sr. Minister & Elder";
NSLog(#"Row 1 Name");
emailaddress = #"Row1Email#att.net";
}
...
}
When I click on the Row 1 name, my console shows "whosthererow1name" and the other NSLog fires off Row 1 Name, however the image doesn't show up, and neither does the text. I have this built using IB and XIB file. I made sure that the Class matched up and that the IBOutlet for both the UILabel and UIImageView were connected. They were.
Why would this not be working?
UPDATE:
Just tested this on a 6.1 device and 7.0 device. 6.1 it works fine, 7.0 it shows no image.
The reason this doesn't work is because the label and the image view will be null at the time you call changeProductText: since nextController's view hasn't been loaded yet. What I don't understand is why it worked in iOS 6. Are you sure you did it exactly this way in iOS 6? The way to do this is to pass a string (and anything else you want) to a property you create in nextController, and populate the UI elements in viewDidLoad.
Is the UIImageView actually initialized and on the screen somewhere? Does the image file actually exist? Make sure that both the UIImageView and the UIImage you are creating are not nil by setting a breakpoint at the point of assignment in the code.
I've looked at the Apple appPrefs code sample, but that seems to be for navigation controllers only. I'm working with an iPad UISplitViewController that has simple root and detail VCs.
I can change certain settings (colors, date formats, etc) but currently, I have to restart the app to have the changes effected. I would prefer not to have to restart the app.
I'm using a system of loading the settings when the app starts each time. I can get a notification system to work, but I don't know how to reload the view controllers.
Any ideas how to do this (I guess reload the views somehow).
Thanks for any tips/advice. I can post some code if relevant.
If you use settings bundle to manage preferences from the Settings app:
From what you said in your question, you already know how to get a notification(UIApplicationDidBecomeActiveNotification) when your app becomes active, right?
If so, the only problem left is how to reload your view after you receive the notification. Other than UITableView, which can be easily reloaded by calling [tableView reloadData], you have to reload your view by assigning values to the UI controls that you want to reload just as you set them up initially. Say you have a UILabel label you want to reload with the newly set preference value, you just write code like this:
- (void)reloadView {
label.text = [[NSUserDefaults standardUserDefaults] stringForKey:#"PreferenceKey"];
self.view.background = …
[self.tableView reloadData];
}
- (void)reloadViewOnAppActivation:(NSNotification *)notif {
[self reloadView];
}
If you are using in app preferences setting:
If the preferences view controller does not display simultaneously with the SplitViewController. Reload your views in their controllers' viewWillAppear: methods:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self reloadView]; // See the definition of reloadView above
}
Otherwise, make the SplitViewController the delegate of, or assign it to an ivar of, the preferences view controller, and notify it of the preferences changes when appropriate — immediately after changing any single preference if you prefer in realtime update, or after all the changes are done if you prefer batch update:
// SplitViewController methods:
- (void)preferencesAreChanged {
[self reloadView]; // See the definition of reloadView above
}
// Preferences view controller methods:
// Immediate update, use a preference controlled by a `UISegmentedControl` as an example
- (void)viewDidLoad {
[super viewDidLoad];
…
[segmentedControl addTarget:self action:#selector(xPreferenceTogglingAction:) forControlEvents:UIControlEventValueChanged];
…
}
- (IBAction)xPreferenceTogglingAction:(id)sender {
// Update the x preference.
…
[delegate preferencesAreChanged];
}
// Batch update
- (void)viewWillDisappear:(BOOL)animated {
[delegate preferencesAreChanged];
[super viewWillDisappear:animated];
}
So, to help others, I will post how I (with help from Apple) solved this.
In both root and detail view controllers, I added in styles based on user settings:
"Warm Tones", "Cool Tones", "Leather" etc. These translate to code like this:
switch (styleKey) {
case 0: // BASIC
fontName = #"Copperplate";
fontSize = 16;
selectedBarColor = [UIColor lightGrayColor];
selectedTintColor = [UIColor lightGrayColor];
selectedFontColor = [UIColor darkGrayColor];
backgroundColor = [UIColor whiteColor];
selectedHighlightColor = UITableViewCellSelectionStyleGray;
backgroundImage = nil;
detailBackgroundImage = nil;
break;
Then, whenever a color/style/font is called, I used something like this:
cell.selectionStyle = selectedHighlightColor;
cell.backgroundColor = backgroundColor;
This allowed me to change the settings and styles, but I still had to restart the app each time to see the changes.
The fix turned out to be simple.
Settings the styles changed the values of the constants (e.g. fontColor) - but I wasn't actually changing the fields.
So at the end of the switch statements, all I added was something like this:
self.tableView.backgroundColor = backgroundColor;
self.navigationController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:backgroundImage]];
self.navigationController.navigationBar.tintColor = selectedBarColor;
self.tableView.separatorColor = selectedTintColor;
I had to do this in both view controllers.
Also, all this code was part of a routine (changeSettings).
This method is being observed to look for changes.
The way I handled the in-app preference look and feel (a modal VC) was to use the terrific InAppSettingsKit.
I hope this helps others. Most of you will find this a no-brainer I expect, but - having not much brain left - it took me two weeks to figure it out.