Where does this backBarButtonItem come from again? - uinavigationcontroller

I have a UINavigationController (A) which has a few subviews which also are UIViewControllers (B and C). The main UINavigationController (A) rides inside of a UITabViewController (D).
I'm trying to push a view controller within B:
[self.navigationController pushViewController... etc]
Now, the backBarButtonItem comes through with the wrong text. Instead of saying 'Back', it just says 'Item'. This is likely because one of the view controllers in my chain has its title set to 'Item' or maybe it is nil altogether.
My question is, where is the backBarButtonItem generated from?
I tried a few different things that didn't work. I tried each of these lines of code within B right before I pushed the view controller. None of them worked.
self.presentingViewController.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back"... etc
self.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back"... etc
self.navigationController.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back"... etc
I'd like to learn the principle here so that I truly understand where this item is being populated from and what the right way to do it is.

Let's say your C controller is on top of the navigation controller's stack, and your B controller is under that. E.g.
navigationController.viewControllers = #[ bViewController, cViewController ];
So the navigation controller is displaying cViewController.view.
The navigation controller uses the second-to-top controller on its stack to configure the back button. In this case, it uses bViewController to configure the back button. This is its algorithm:
UINavigationItem *navigationItem = bViewController.navigationItem;
UIBarButtonItem *barItem = navigationItem.backBarButtonItem;
if (barItem.image != nil) {
show a back button containing barItem.image;
}
else if (barItem.title != nil) {
if (barItem.title.length > 0) {
show a back button containing barItem.title;
} else {
don't show a back button;
}
}
else if (navigationItem.title != nil) {
if (navigationItem.title.length > 0) {
show a back button containing navigationItem.title;
} else {
don't show a back button;
}
}
else {
show a back button containing #"Back";
}

Related

UICollectionViewCell custom subview doesn't receive focus

I created custom Poster view so it can be reused in multiple collection view cells (just like TVPosterView in TVUIKit). I add it directly to cell content view with all needed constraints.
The problem is when cell is focused this subview doesn't receive focus update (didUpdateFocus..) so I cannot customize it's focused / unfocused constraints etc. It's odd btw that image view inside is getting floating effect.
In case if I specify cell's preferredFocusEnvironments to return [self.posterView] + super. preferredFocusEnvironments, UI behaves as expected, but the collection view delegate method didSelect not called!
Thanks in advance for any help!
Seems didUpdateFocus not called on all subviews for the focused cell and it's system design. From docs:
After the focus is updated to a new view, the focus engine calls this
method on all focus environments that contain either the previously
focused view, the next focused view, or both, in ascending order. You
should override this method to update your app’s state in response to
changes in focus. Use the provided animation coordinator to animate
changes in visual appearance related to the update. For more
information on animation coordinators, see
UIFocusAnimationCoordinator.
Note: So it means didUpdateFocus will be called first on UICollectionViewCell, than on UIViewController subclasses, in ascending order. For subviews you need to manually register customDidUpdateFocus method that will be triggered in notification update. E.g. to update it's appearance we can use notifications (tvOS 11+), please see the example below.
func customDidUpdateFocus(isFocused: Bool, with coordinator: UIFocusAnimationCoordinator) { /* Custom logic to customize appearance */ }
// Register observer
observerToken = NotificationCenter.default.addObserver(forName: UIFocusSystem.didUpdateNotification, object: nil, queue: .main) { [weak self] (note) in
guard let self = self else { return }
guard let context = note.userInfo?[UIFocusSystem.focusUpdateContextUserInfoKey] as? UIFocusUpdateContext else { return }
guard let coordinator = note.userInfo?[UIFocusSystem.animationCoordinatorUserInfoKey] as? UIFocusAnimationCoordinator else { return }
if let prev = context.previouslyFocusedView, self.isDescendant(of: prev) {
self.didUpdateFocus(isFocused: false, with: coordinator)
} else if let next = context.nextFocusedView, self.isDescendant(of: next) {
self.didUpdateFocus(isFocused: true, with: coordinator)
}
}

Reproduce button action on tab page

On LineDetails form we have tab page [Areas]. Under that tab page is button [Read Areas] . That button is redundant and there is no need for it, as that action which is occurred when clicked on it should be done when tab page [Areas] is opened.
[Control("Button")]
class ReadCommonAreas
{
void clicked()
{
ContractArea contractArea;
AmountMST sumContractArea;
super();
ContractLine_ds.readCommonAreas(ContractLine);
h1_h2.realValue(ContractLine_ds.h1_h2(ContractLine));
efa.realValue(ContractLine_ds.efa(ContractLine));
bfa.realValue(ContractLine_ds.bfa(ContractLine));
mfa.realValue(ContractLine_ds.mfa(ContractLine));
sumArea.realValue(h1_h2.realValue() + efa.realValue() + bfa.realValue() + mfa.realValue());
while select AreaSelector, sum(RentalValue)
from contractArea
group by AreaSelector
where contractArea.ContractId == Contract.ContractId
&& contractArea.RentalObjectId == ContractLine.RentalObjectId
{
sumContractArea += contractArea.RentalValue;
switch (contractArea.AreaSelector)
{
case AreaSelector::CommonAreaBuilding :
contractAreaBFA.realValue(contractArea.RentalValue);
break;
case AreaSelector::CommonAreaSection :
contractAreaEFA.realValue(contractArea.RentalValue);
break;
case AreaSelector::PrimaryArea, AreaSelector::SecondaryArea :
contractAreaH1_H2.realValue(contractArea.RentalValue);
break;
case AreaSelector::CommonAreaFixed :
contractAreaMFA.realValue(contractArea.RentalValue);
break;
}
}
contractAreaSum.realValue(sumContractArea);
}
}
How to adapt this, when is clicked on Areas to show info like when it's clicked on Read Areas ?
And after that, I will remove Read Areas button.
Just make the Read Areas button Visible = No to hide it.
Then on the Areas tab page, override the pageActivated() method and do ReadAreas.clicked(). That way the core code stays in tact.
The thing you should consider is if the user tabs off/on the Areas page, every time it will click the Read Areas button...not sure if that's a problem or not.
Use event handlers and the like where appropriate. This is just off the top of my head general approach.

Focus Ring in field editor for NSTextFieldCell in an NSTableView

I have a cell-based NSTableView with a text cell in a particular column. I want to provide a custom field editor so that I can do auto completion without the user pressing F5.
#implementation AutoCompleteFieldEditorTextView
-(id)init
{
self = [super init];
if (self)
{
self.focusRingType = NSFocusRingTypeDefault;
self.fieldEditor = YES;
}
return self;
}
#end
This works ok except the focus ring does not exist. Even if I add:
-(void)drawFocusRingMask
{
NSRectFill([self bounds]);
}
-(NSRect)focusRingMaskBounds
{
return [self bounds];
}
it still does not work. How can I get the exact focus ring that appears with the default NSTextView used as a field editor in an NSTableView?
Have same problem and for solving I'm using next approach. This method overloaded in custom NSTextFieldCell which creates and return your custom NSTextView:
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
[super drawInteriorWithFrame:cellFrame inView:controlView];
if([controlView.window firstResponder] == self.maskTextField)
{
[super drawFocusRingMaskWithFrame:cellFrame
inView:controlView];
}
}
Hope this help someone in future.

UIButton to Navigation title

I have Two ViewController with Navigation controller,
and i want to press different button to change to next page and Next title also change when i hit a different button?
used this method??
- (void)viewDidLoad {
if Button == A;{
self.title = #"A";
}else Button == B {
self.title = #"B";
}
- (IBAction)A:(id)sender {
self.title =#"A";
}
- (IBAction)B:(id)sender {
self.title =#"B";
}
I know this is a wrong way, cause it just change first page title,but i want to change the second page only,and i just want to description that what i want to do,
please help,
Thanks
Pass your string (Which you want to set on next View Controller) from your first view controller to next view controller and put this self.title = Your passed string in next view controller's viewDidLoad()

UINavigationCotroller backBarButtonItem with other target than self

I am trying to change the action executed by pressing the backBarButtonItem of a Navigation Controller.
I know that i have to edit the backBarButtonItem before the next view (on which the button with custom behavior should appear) is pushed. So in the previous ViewController i added the following code, to push via segue:
#pragma mark - segue methods
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SettingsToProfile"]) {
MyProfileViewController* myprofileVC = [segue destinationViewController];
myprofileVC.myProfile = myProfile;
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Settings_" style:UIBarButtonItemStylePlain target:myprofileVC action:#selector(popTOSettingsViewController:)];
} else {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:UIBarButtonItemStylePlain target:nil action:nil];
}
}
The title "Settings_" gets displayed correctly, but "target: myprofileVC action:#selector(popTOSettingsViewController:)" doesn't seem to have any effect at all.
Background:
MyProfileViewController is a View, where the user can edit his/her own information. Whenever the backbarbutton is clicked, i want to check if something in the GUI has been changed by the user. If thats the case, a UIAlterView should ask the user, if he/she wants to save the changing. I tried to work it out with, viewWillDissappear, but the AlterView gets displayed in the next ViewController (and program crashes, if i click on the alterViewButtons).
I'm not sure that you can change the target of a backBarButtonItem. How about self.navigationItem.leftBarButtonItem instead? There's a discussion at self.navigationItem.backBarButtonItem not working ?? Why is the previous menu still showing as the button? that may be relevant.

Resources