UITableview adding white space at top of view - swift

I am running into a weird issue with a tableview adding an empty "white space" at the top. I have the table view constraint bound to 5 of the segmented control field above.
I am new to Swift and I am not sure how to further debug these types of UI issues. I have checked the constraints and I do not think that is the issue. The storyboard does not show this additional white space... where is it coming from?
EDIT: It appears to only create the whitespace on iOS10. Looks fine on iOS11.
EDIT: xCode screen

EDIT: I see someone else took my code and got selected already but for the sake of providing full answer here it is.
This behavior is caused by automatic insets by the ios platform. There are two options here:
If you snap your table view to bottom edge of navbar be sure to execute the code below. It will disable automatic insets on both iOS 11 and older iOS versions.
Otherwise you can snap your tableview to edge of the view and omit the code, because the purpose of the code is to compensate the size of navbar/tabbar, and since you snap your tableview behind/under them, you need that compensation to happen.
Code in case of #1 scenario that works on iOS 11 and older platforms.
Objective-c:
if (#available(iOS 11, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
self.automaticallyAdjustsScrollViewInsets = NO
}
Swift:
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}

It seems that the automatic content insets on the table view are activated, you can stop this behavior by adding this code to your view controller.
if #available(iOS 11, *) {
self.tableView.contentInsetAdjustmentBehavior = .never;
}else{
self.automaticallyAdjustsScrollViewInsets = false
}

Try to look in the 'attribute inspector' (in the right menu) of the Participants ViewController. Check for the option 'Extend Edges' and uncheck the 'Under Top Bars', and then relocate your tableview.

This is the updated 2022 iOS 15 solution
if #available(iOS 15.0, *) {
UITableView.appearance().sectionHeaderTopPadding = CGFloat(0)
}

Related

Disable Border of cell in iOS 16 / Xcode 14

iOS 16/Xcode 14 adds a blue border around cells in sidebar. How can this be removed?
My cell is a custom class derived from UICollectionViewListCell.
allowsFocus is a new property in iOS 15. Its use is covered in this WWDC video: Build Desktop-class iPad app (see minute ~15:25). Perhaps the default (or the implementation) changed in iOS 16. In any case, setting it to false removes the border.
if #available(iOS 15.0, *) {
collectionView.allowsFocus = false
}
To add to #Phantom59's answer. You can still use allowsFocus without the focus border by setting the UICollectionViewCell's focusEffect to nil:
if #available(iOS 15, *) {
cell.focusEffect = nil
}
More info: Focus-based navigation

Update to Xcode 11.3.1 - navigationBar and half of the Views disappear after storyboard refactoring

Using Xcode 11.3.1, Simulator11.3.1, iPhoneX, Swift5.1.3, iOS13.3,
I am wondering why half of my app suddenly disappears !!
Could it be the update to Xcode 11.3.1 ???
The following shows a screenshot of the Xcode Debug View Hierarchy.
The left side is what the iPhone 11 Pro Simulator shows and the right side is the Debug View Hierarchy:
Clearly there are many more objects in the view hierarchy (such as the round buttons at the bottom) that are not shown on the Simulator (and also not on a physical iPhoneX). Also the NavigationBar is missing completely !!!!
The blue highlighted object is a custom navigationBar (consisting of a stackView). This worked before but not since the Xcode update. I am really not believing this. What could go wrong here ??
If it is not the Xcode-update, then my refactoring of the storyboard could also be a cause of this view-losses.
Before my refactoring, the VC at question was a ChildViewController of another ViewController. Now, it is the entry point of the App. Could this change bring the view-losses ? I want to see a NavigationController with largeTitle. But there is no NavigationController whatsoever now!
Here is the code that sets up the navigationBar:
override func viewDidLoad() {
// set up navigationItem and navigationController look and feeel
navigationItem.largeTitleDisplayMode = .always
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationController?.set_iOS12_lookAndFeel()
navigationItem.title = "bluub"
}
And the needed NavigationController extension:
import UIKit
extension UINavigationController {
func set_iOS12_lookAndFeel() {
if #available(iOS 13.0, *) {
self.keep_iOS12_lookAndFeel()
} else {
let attrLargeTitle = AppConstants.FontAttributes.NavBar_LargeTitleTextAttributes
self.navigationBar.largeTitleTextAttributes = attrLargeTitle
let attrTitle = AppConstants.FontAttributes.NavBar_TitleTextAttributes
self.navigationBar.titleTextAttributes = attrTitle
}
}
private func keep_iOS12_lookAndFeel() {
if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithDefaultBackground()
navBarAppearance.backgroundEffect = .init(style: .systemThickMaterialDark)
navBarAppearance.titleTextAttributes = AppConstants.FontAttributes.NavBar_TitleTextAttributes
navBarAppearance.largeTitleTextAttributes = AppConstants.FontAttributes.NavBar_LargeTitleTextAttributes
navBarAppearance.buttonAppearance.normal.titleTextAttributes = AppConstants.FontAttributes.NavBar_ButtonAppearance_Normal
navBarAppearance.doneButtonAppearance.normal.titleTextAttributes = AppConstants.FontAttributes.NavBar_Done_ButtonAppearance_Normal
self.navigationBar.standardAppearance = navBarAppearance
self.navigationBar.scrollEdgeAppearance = navBarAppearance
}
}
}
.
---------------- more findings -----------------------------
After another storyboard refactoring, I could bring back the round menu buttons. However, the largeTitle-NavigationBar is still completely missing.
Frankly, the latest refactoring did not introduce any new constraints or other storyboard settings as before. The fact that I kicked out the NavigationController and replaced it by an identical new one, plus, re-assigned one or the other constraint of the menu-button-View, did bring the bottom menu back alive. As far as I can tell, no difference to the previous storyboard was introduced.
It is very annoying why a storyboard needs to be redrawn basically to render correctly. Something seems corrupt here as for the Xcode functionality with storyboard !
But lets leave this talk.
My remaining question:
How can I bring back a missing NavigationBar ?????????
.
---------------- another finding -----------------------------
If I reassign the "first-entry-ViewController" to the old ViewController that eventually adds the Menu-button-ViewController as a ChildViewController --> then everything works!
If I assign the "first-entry-ViewController" to be the Menu-button-ViewController directly, then the NavigationBar disappears !
Here is the overview:
I finally found a solution.
It indeed had to do with my login-architecture of this app.
The fact that only by setting the "first-entry-ViewController" as the old-Main-ViewController made a difference:
This old-Main-ViewController (that eventually adds the Menu-button-ViewController as its Child) did have the following line in its viewWillAppear method:
navigationController?.setNavigationBarHidden(true, animated: animated)
Its intention was actually to never show the navigationBar of its own. But instead load a ChildViewController that itself shows a navigationBar of its own.
The strange thing with storyboard: Even tough setting the Menu-button-ViewController as first-entry does somehow still consider the navigationController-hiding mechanism of the previous first-entry setting. This seems a bug to me inside storyboard. I would assume that visible navigationBar is the default behaviour. But having set it once to be hidden keeps it hidden, even tough the hiding-command is no longer executed. Anyway, very strange behaviour.
By eliminiting that line - or better - by adding it "with hidden = false" inside the Menu-Button-ViewController, makes the NavigationBar being shown again !!!
My learning is to keep an eye on all navigationController actions or mutations throughout the entire App hierarchy. The fact that a single ViewController might mutate something on its navigationController might not be enough. You have to check event parent-ViewControllers or segue-parents as well. And most annoying, applying a different first-entry to a VC does require you to overwrite default behaviours of your views to make sure your views are shown !

Is this new navigation bar behaviour in xcode 11 beta a bug or intended?

I noticed after compiling one of my apps in Xcode 11 beta, that navigation bars have no background when prefersLargeTitles is set. Is this intended behavior?
I noticed this is how the messages app works now when scrolling down and a large title is visible there is no nav bar background.
Here is the code used to set up the navBar attributes:
override func viewWillAppear(_ animated: Bool) {
let textAttributes = [NSAttributedString.Key.foregroundColor:ThemeManager.shared.default1]
self.navigationController?.navigationBar.largeTitleTextAttributes = textAttributes
self.navigationController?.navigationBar.titleTextAttributes = textAttributes
self.navigationController?.navigationBar.tintColor = ThemeManager.shared.default1
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.navigationController?.navigationBar.prefersLargeTitles = true
let nav = self.navigationItem
nav.title = "My Profile"
}
Here are a couple of images showing the difference:
left, compiled on Xcode 10, right, Xcode 11 beta:
Once you scroll up on the 11 Beta version, the background fades back in. Note that apps that are not compiled in Xcode 11 beta will still behave in the normal way, only changes after compiling for some reason. Is this intended, and how would I bring back the original behavior?
This is intended behavior for iOS 13.
Apple's idea (terrible in my opinion) is that the title should merge with the content to show that it is related. Once you start scrolling, when content goes behind the title bar then the title bar will take the "correct" appearance.
The reason this is terrible is because everyone has currently planned all of their UI without this behavior. So the new behavior should be opt-in instead of forcing everyone to opt-out (i.e. the change breaks everyone's code and if you're going to break everyone's code at least you should be clear about how to keep the tried and true behavior of the last 10 years).
As in your case, the result looks horrible. The result looks horrible in my case too.
Apple doesn't give answers but says that you should be using
- scrollEdgeAppearance
From UINavigationBar in order to control the appearance of the bar when content is aligned top-of-content to bottom-of-navbar ... in my case this method returns nil though so I'm currently unsure how we're supposed to use this.
This seems to be discussed here as well:
New UINavigationBar appearance in detail pane of UISplitViewController in iOS 13
So the current workaround would seem to be this in your view controller:
- (void)viewDidLoad;
{
[super viewDidLoad];
if (#available(iOS 13,*)){
UINavigationBar *bar =self.navigationController.navigationBar;
bar.scrollEdgeAppearance = bar.standardAppearance;
}
}
It works, but if it's the intended approach, I don't know...
EDIT:
Doing this does seem to block any additional direct customization to the UINavigationBar as has been noted. Possible that adjusting the scrollEdgeAppearance from here is the way to go. Ugly. Ugly. Ugly.
EDIT: Progress... this is working now for managing the background. You need to call this instead of setting barTint directly.
#interface UINavigationBar (Compatibility)
- (void)setCompatibleTint:(UIColor *)fg andBarTint:(UIColor *)bg;
#end
#implementation UINavigationBar (Compatibility)
- (void)setCompatibleTint:(UIColor *)fg andBarTint:(UIColor *)bg;
{
self.tintColor = fg;
self.barTintColor = bg;
if (#available(iOS 13,*)){
// we need to tell it to adopt old style behavior first
UINavigationBarAppearance *appearance = self.standardAppearance;
appearance.backgroundColor = bg;
NSDictionary *attributes = self.titleTextAttributes;
appearance.titleTextAttributes = attributes;
attributes = self.largeTitleTextAttributes;
appearance.largeTitleTextAttributes = attributes;
self.scrollEdgeAppearance = appearance;
self.standardAppearance = appearance;
self.compactAppearance = appearance;
}
}
#end
I'm not entirely sure yet on the text attributes but it seems to flow from the background color. It's a complete PITA.
It would be nicer to set this as a subclass and override barTint but of course a lot of the UIKit objects create these bars themselves so you won't get the subclass.
Swift version of dbquarrel's solution.
First declare your textAttributes:
let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.red]
Use these in a UINavigationBarAppearance() to enable you to change the colour of the text in 3 different modes (scollEdge, standard and compact).
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.largeTitleTextAttributes = textAttributes
appearance.titleTextAttributes = textAttributes
let bar = self.navigationController?.navigationBar
bar?.scrollEdgeAppearance = appearance
bar?.standardAppearance = appearance
bar?.compactAppearance = appearance
} else {
// Fallback on earlier versions
}
}

a header unexpectedly shows up in all view controllers in iOS 10 and lower

all my view controllers and constraints are fine in iOS 11 and above but in iOS 10 and below a space created in all pages like the pictures below :
I tried even a simple web view in a viewcontroller with four constraints to safe area but I've got the same result.(good view in iOS 11 and above and a space to top in iOS 10 and below. and another strange thing is that some of my apps now have this problem and some don't :| . what should I do?
Can you try this?
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
Note: Use (tableView, collectionView) instead of scrollview if you are using one.

iOS7 UIScrollView show offset content below status bar

I'm developing my app to work with iOS7.
I have a UINavigationController I'm pushing a UIViewController that has a ScrollView inside it. Inside the scrollView I have a tableView.
Is it possible to achieve that when I scroll the tableView inside the scrollView the list will appear behind that Status bar. Same why it would be if I had a UINavigationController and a UIViewController with a tableView in it.
So this it the hierarchy :
UINavigationController -> UIViewController -> UIScrollView -> UITableView .
and I want that when a user scroll the table,the cells in the top will be visible under the status bar.
If there is no UIScrollView it happens automatically in iOS7.
Thanks.
Just set automaticallyAdjustsScrollViewInsets to NO in the viewController init method.
In Storyboard, you can switch the property directly in the property panel when the UIViewController is selected.
If you use xib, just set it like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self.automaticallyAdjustsScrollViewInsets = NO;
}
Note: this is right since iOS7 and still in iOS8.
none of the above workd for me, until I noticed that I had to set Content Insets from Automatically to Never in the interfacebuilder:
Starting with iOS 11 you can use this new property with a fallback (Swift 4):
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
The answer from Skoua might work in some situations, but does have certain side-effects on iOS11 and later. Most notably, the scroll view will start propagating safe areas to its children, which can mess up your layout while scrolling if you use the safe areas or layout margins.
Apple explains this very well and in detail in this WWDC session and also mentions explicitly that contentInsetAdjustmentBehavior = .never can have side-effects and is not what you should use in most cases.
To get a scroll view that does not mess up our layout, but shows its content below the status bar (or navigation bar), you should observe the safe area of your scroll view and adjust your custom content insets accordingly:
private var scrollViewSafeAreaObserver: NSKeyValueObservation!
override func viewDidLoad() {
...
if #available(iOS 11.0, *) {
self.scrollViewSafeAreaObserver = self.scrollView.observe(\.safeAreaInsets) { [weak self] (_, _) in
self?.scrollViewSafeAreaInsetsDidChange()
}
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
}
#available(iOS 11.0, *)
func scrollViewSafeAreaInsetsDidChange() {
self.scrollView.contentInset.top = -self.scrollView.safeAreaInsets.top
}
deinit {
self.scrollViewSafeAreaObserver?.invalidate()
self.scrollViewSafeAreaObserver = nil
}
Why does this work? Because we leave contentInsetAdjustmentBehavior = .automatic. This will give us normal behaviour when it comes to scrolling and non-scrolling axis, but the UIScrollView will also "convert" any safe areas to content insets. Since we don't want this for our top edge, we simply set the negative top safe area as our custom insets, which will counter any insets set by the scroll view.
Thats just dumb from Apple. One more weird behaviour to worry about. Anyhow, I ended up setting the scroll view content inset for top to -20 (from IB).
I found the solution! Just set:
self.automaticallyAdjustsScrollViewInsets = false
on the view controller that has the UIScrollView.
You probably has seen this recommendation a thousand times but, check the 'The Status Bar' section on the iOS 7 transition guide(can't post the direct link here).
Blunt resuming, on ios7 the status bar is part of the view. This means that almost anything you put on your view, will be under the bar, unless you say the contrary. A work around i found for that problem was making the status bar opaque.
Another way could be disabling the status bar on that specific view. Like on this thread.
I have had a similar sort of problem, and I found that to ensure my scrollview content doesn't go under the status bar, I applied a setContentInset.
So in your situation, if you are using an inset, I would use suggest using UIScrollViewDelegate or scrollViewDidScroll tailored to your tableview. If a scroll is occurring, disregard the scrollview inset.
don't hide the statusBar, the scrollView won't jump