ios 11 navigation bar overlap status bar - ios11

In ios 11 navigation bar is overlapping status bar. If any body faced the same issue kindly help.

Not sure if this is the same issue, but we ran into this as well when upgrading to iOS 11.
See ios 11 custom navbar goes under status bar
We were manually setting nav bar height to 64 and pinning to the superview edges. Conforming to the UINavigationBarDelegate protocol and implementing the UIBarPositioningDelegate delegate method solved it for us.
We replaced
navigationBar.autoPinEdgesToSuperviewEdgesExcludingEdge(.bottom)
navigationBar.autoSetDimension(.height, toSize: 64)
with
...
if #available(iOS 11.0, *) {
navigationBar.topAnchor.constraint(
equalTo: self.view.safeAreaLayoutGuide.topAnchor
).isActive = true
} else {
navigationBar.topAnchor.constraint(
equalTo: topLayoutGuide.bottomAnchor
).isActive = true
}
navigationBar.autoPinEdge(toSuperviewEdge: .left)
navigationBar.autoPinEdge(toSuperviewEdge: .right)
navigationBar.delegate = self
...
public func position(for bar: UIBarPositioning) -> UIBarPosition
return .topAttached
}
This is using the purelayout DSL for some of the autolayout calls (https://github.com/PureLayout/PureLayout)
Credit goes to https://stackoverflow.com/users/341994/matt for the answer

Had similar problem. In my case it turned out that previous view controller had custom nav bar and therefore it was hiding both - nav bar and status bar. There was
UIApplication.shared.setStatusBarHidden(true, with: UIStatusBarAnimation.none)
UIApplication.shared.setStatusBarStyle(.default, animated: false)
And in the problematic view controller I had this:
UIApplication.shared.setStatusBarStyle(.default, animated: false)
UIApplication.shared.setStatusBarHidden(false, with: UIStatusBarAnimation.none)
The issue was fixed simply by putting the two lines in the correct order:
UIApplication.shared.setStatusBarHidden(false, with: UIStatusBarAnimation.none)
UIApplication.shared.setStatusBarStyle(.default, animated: false)
All things above are deprecations, so another possible fix would probably be changing this to the recommended way of hiding status bar (which is not yet ideal as discussed here: setStatusBarHidden deprecated, but only thing that works).

Set child view to top constraint of superview... Click for edit constraint If you see "Align Top to : Safe Area " change it to superview so that it will overlap

Related

UINavigationBar appearance tint color affects system windows, bug or intended?

In iOS 10 one can set the tint color of the navigation bar for all the app using this line of code:
UINavigationBar.appearance().barTintColor = UIColor.green
However when the same is done in iOS 11, it seems to also affect system views. For example, when showing a UIActivityViewController with this code:
let titleText = "SOME TITLE"
let urlStr = "https://google.com"
let activityItems = [titleText,urlStr]
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
And selecting the "Add to Notes" option, the navigation bar is shown with the previously set color (green in this case)
I would like to ask, is this is a bug or is it intended?
(I already filed a bug report to apple a little over a month ago but didn't get a response)
Here is a screenshot of the issue:
Why not simply Subclass UINavigationController and set the appearance proxy like so?
UINavigationBar.appearance(whenContainedInInstancesOf: [YourUINavigationControllerSubclass.self]).barTintColor = UIColor.green
This a lot less hacky and it will solve the issue without any negative effects.
You can find a bit more info on the subject here.

UIRefreshControl not showing spiny when calling beginRefreshing and contentOffset is 0 [duplicate]

This question already has answers here:
UIRefreshControl - beginRefreshing not working when UITableViewController is inside UINavigationController
(15 answers)
Closed 9 years ago.
I am not able to see the loading spinner when calling beginRefreshing
[self.refreshControl beginRefreshing];
My UITableViewController subclass uses a UIRefreshControl
// refresh
UIRefreshControl * refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:#selector(refreshTableView) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
It is working perfectly with user interaction (when the user drops the table down), then the spinner is visible.
But when i call beginRefreshing on viewDidLoad, I don't see the spinner (only when i drag the table down).
Notes:
self.refreshControl reference is right
reloadData or endRefreshing is not called immediately after beginRefreshing, but there is a long time delay (loading data through network), so I am not canceling the beginRefreshing.
Edit :
This only happens when the contentOffset property of the tableView is 0 and i call [self.refreshControl beginRefreshing]. Bug? Feauture?
It looks like a bug to me, because it only occures when the contentOffset property of the tableView is 0
I fixed that with the following code (method for the UITableViewController) :
- (void)beginRefreshingTableView {
[self.refreshControl beginRefreshing];
if (self.tableView.contentOffset.y == 0) {
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
} completion:^(BOOL finished){
}];
}
}
Your fix looks good, But I don't think this as a bug.
When beginRefreshing method is called manually,
When there is no row / cell available it makes sense for refresh control appearing automatically.
But when there are some cells available, and when we call begin refresh manually (A scenario where we refresh periodically based on timer) then It should not animate / change the content offset as it will distract the user if he is seeing / reading content in some visible cell.

Custom segue with left to right animation going diagonal instead

I'm building an app where the first view has a menu panel, and I want this panel to stick around for the life of the app. The only places the user can "go" are reachable via buttons on this panel (a UICollectionView). In case it matters, this app is landscape-only, and iOS 6-only.
In order to make this work I created a custom segue, which removes everything from the view except for the menu panel, then adds the new view controller's view as a subview, sets the new view's frame to the bounds of the superview, and sends the new view to the back (so it's behind the menu panel). I call viewWill/DidDisappear from prepareForSegue, because otherwise they don't get called.
It may sound kludgy (it does to me), but it works fine except for one thing - the new view comes up from the bottom. It looks funny.
I then tried adding my own animation block - I initially locate the view off to the left, then animate it into place. I send it to the back in the completion block for the animation. This seems perfectly logical, and the frame values are all what they should be. But this one is worse - the view comes in from the lower left corner.
Can anyone suggest a way to make this work? Here's my current perform method:
- (void)perform {
MainMenuViewController *sourceVC = (MainMenuViewController *)self.sourceViewController;
UIViewController *destinationVC = (UIViewController *)self.destinationViewController;
for (UIView *subview in [sourceVC.mainView subviews]) {
// don't remove the menu panel or the tab
if (![subview isKindOfClass:[UICollectionView class]] && [subview.gestureRecognizers count] == 0) {
[subview removeFromSuperview];
}
}
[sourceVC.mainView addSubview:destinationVC.view];
CGRect finalFrame = sourceVC.mainView.bounds;
CGRect frame = finalFrame;
frame.origin.x = finalFrame.origin.x - finalFrame.size.width;
destinationVC.view.frame = frame;
[UIView animateWithDuration:0.5 animations:^{
destinationVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
// make sure it ends up behind the main menu panel
[sourceVC.mainView sendSubviewToBack:destinationVC.view];
}];
}
It turned out to be a simple error - I needed to set destinationVC's frame before calling addSubview. Setting it afterwards was triggering the unwanted animation.

iOS 6: UITextField textAlignment does not change cursor position

I use an instance of UITextField in which the text is normally aligned to the right, but switches to left alignment when being edited. This is done by calling setTextAlignment: on the editingDidBegin event. Since the update to iOS 6 this behaves strangely: The text alignment is changed correctly from right to left, but the cursor remains at the far right of the text field until some input is performed.
Does anybody know how to restore the expected behaviour so that the cursor moves as well when the alignment is changed?
To give some context: I use the text field to show a value with a unit. The unit is removed during editing and then displayed again after the user hits enter.
Method called on event editingDidBegin:
- (IBAction)textEditingDidBegin:(UITextField *)sender
{
[sender setTextAlignment:NSTextAlignmentLeft];
[sender setText:[NSString stringWithFormat:#"%.0f", width]];
}
Method called on event editingDidEnd:
- (IBAction)textEditingDidEnd:(id)sender
{
[sender setTextAlignment:UITextAlignmentRight];
[sender setText:[NSString stringWithFormat:#"%.0f m", width]];
}
Try resigning the textView as first responder, then make it first responder right after that.
textView.textAlignment = NSTextAlignmentCenter;
[textView resignFirstResponder];
[textView becomeFirstResponder];
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
textField.textAlignment = NSTextAlignmentLeft ;
return YES ;
}
Change the textAlignment before editing did begin. The best place I know to do this is in the UITextFieldDelegate method textFieldShouldBeginEditing.

Pull-to-refresh in UICollectionViewController

I want to implement pull-down-to-refresh in a UICollectionViewController under iOS 6. This was easy to achieve with a UITableViewController, like so:
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(startRefresh:)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
The above implements a nice liquid-drop animation as part of a native widget.
As UICollectionViewController is a "more evolved" UITableViewController one would expect somewhat of a parity of features, but I can't find a reference anywhere to a built-in way to implement this.
Is there a simple way to do this that I'm overlooking?
Can UIRefreshControl be used somehow with UICollectionViewController despite the header and docs both stating that it's meant to be used with a table view?
The answers to both (1) and (2) are yes.
Simply add a UIRefreshControl instance as a subview of .collectionView and it just works.
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(startRefresh:)
forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:refreshControl];
That's it! I wish this had been mentioned in the documentation somewhere, even though sometimes a simple experiment does the trick.
EDIT: this solution won't work if the collection is not big enough to have an active scrollbar. If you add this statement,
self.collectionView.alwaysBounceVertical = YES;
then everything works perfectly. This fix taken from another post on the same topic (referenced in a comment in the other posted answer).
I was looking for the same solution, but in Swift. Based on the above answer, I have done the following:
let refreshCtrl = UIRefreshControl()
...
refreshCtrl.addTarget(self, action: "startRefresh", forControlEvents: .ValueChanged)
collectionView?.addSubview(refreshCtrl)
Not forgetting to:
refreshCtrl.endRefreshing()
I was using Storyboard and setting self.collectionView.alwaysBounceVertical = YES; did not work. Selecting the Bounces and Bounces Vertically does the job for me.
The refreshControl property has now been added to UIScrollView as of iOS 10 so you can set the refresh control directly on collection views.
https://developer.apple.com/reference/uikit/uiscrollview/2127691-refreshcontrol
UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:#selector(refreshControlAction:) forControlEvents:UIControlEventValueChanged];
self.collectionView.refreshControl = refreshControl;
mjh's answer is correct.
I ran into the issue where if the the collectionView.contentSize was not larger then the collectionView.frame.size, you can not get the collectionView to scroll. You can not set the contentSize property either (at least I couldn't).
If it can't scroll, it won't let you do the pull to refresh.
My solution was to subclass UICollectionViewFlowLayout and overide the method:
- (CGSize)collectionViewContentSize
{
CGFloat height = [super collectionViewContentSize].height;
// Always returns a contentSize larger then frame so it can scroll and UIRefreshControl will work
if (height < self.collectionView.bounds.size.height) {
height = self.collectionView.bounds.size.height + 1;
}
return CGSizeMake([super collectionViewContentSize].width, height);
}

Resources