UITableViewController overlapping bottom safe area - swift

I added a Tableview Controller in storyboard, The problem is that it's overlapping the bottom safe area on iPhone X. I also have a view at the bottom which is above the safeAreaLayoutGuide. I tried to use auto layout and anchor it to view.safeAreaLayoutGuide.bottomAnchor, but this doesn't work. Any help would be appreciated. This is what I've tried, with other options but no help.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true }

Related

Why does NSScrollView display a white track?

I am programming with macos, in swift. I have a few paragraphs of text (Lopem Ipsum - to test) inside a scroll view. Also a title, which is a NSTextView, positioned above.
When I present it in a popover, all looks fine.
However in another part of the project, I present a the same contents (using a copy of the same layout code - below), but this time in a modal window.
It has a ugly white track! That must be connected to the scroll view, since it only appears when I set the vertical scrollbar - hasVerticalScroller, which is necessary because I want vertical scrolling. As it should, the knob highlights on mouse-hover and it scolls properly.
It probably would be fine with a solid white background. But that's not what I want! Thus I have the setting scrollView.drawsBackground = false.
I've tried: scrollView.verticalScroller?.highlight(false); scrollView.verticalScroller?.wantsLayer = true; scrollView.verticalScroller?.layer?.backgroundColor = NSColor.clear.cgColor - no luck!
Even tried with only one paragraph (ie. no scroll), still the same.
I am using code similar to this:
class ViewController: NSViewController {
let scrollView = NSScrollView()
let textView = NSTextView()
override func viewDidLoad() {
super.viewDidLoad()
textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
textView.autoresizingMask = .width
textView.isVerticallyResizable = true
textView.textContainer?.widthTracksTextView = true
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.documentView = textView
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
}
What can I do to have a clear (no background) track on the scrollbar?
scrollView.scrollerStyle = .overlay

How to make mapView and segmentedControl move relative to AdMob adaptive banner

I'm adding an adaptive banner ad from AdMob to the bottom of a mapView in my iOS app. No problem adding it, but the banner obscures both a segmentedControl which is placed at the bottom of the mapView. (Both the mapView and the segmentedControl are inside a tabBarController, all created in Storyboard.) The ad also covers the Apple Mags logo and "Legal" information - not sure if Apple will have a problem with that.
Image example - top of segmentedControl barely visible under ad
How can I make the mapView shift up by the same amount of space taken up by the ad, so as to make the segmentedControl and Apple Maps boilerplate visible, and not shift up at all if no ad is loaded? (Or is there another best practice of handling this?)
All my AdMob code is from the official AdMob tutorial:
func getAdaptiveSize() -> GADAdSize {
var frame: CGRect
if #available(iOS 11.0, *) {
frame = view.frame.inset(by: view.safeAreaInsets)
} else {
frame = view.frame
}
let viewWidth = frame.size.width
return adSize
}
func loadAdaptiveBannerAd(){
bannerView.adSize = getAdaptiveSize()
bannerView.load(GADRequest())
}
func addBannerToView(){
bannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
override func viewDidLoad() {
super.viewDidLoad()
bannerView.adUnitID = "ca-app-pub-3940256099942544/2435281174"
bannerView.rootViewController = self
bannerView.backgroundColor = .darkGray
addBannerToView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadAdaptiveBannerAd()
}
My hunch is that I have to fix this not by changing the placement of the ad banner itself, but by approaching either the mapView or the superview somehow, but I haven't found the solution.
You didn't show your actual layout, but I'm going to assume it looks very close to this:
The Bottom of your mapView is constrained to the Bottom of the safe-area, and the Bottom of your Segmented control is constrained to the Bottom of the mapView. If you have your SegControl also constrained to the safe-area, change that to the mapView -- that way we only have to manipulate the mapView and the SegControl will move with it.
First step, change the Priority of the Map view's bottom constraint to High (750):
and your constraints will look like this:
Next, connect that constraint to an #IBOutlet:
#IBOutlet var mapBottomConstraint: NSLayoutConstraint!
result:
Now at run-time, when you add your banner view, add a NEW Bottom constraint to your map view:
func addBannerToView(){
bannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// add this line
mapView.bottomAnchor.constraint(equalTo: bannerView.topAnchor)
])
}
Because the newly created constraint will have the default Priority of Required (1000), auto-layout will "pin" the bottom of the map view to the top of the banner. Since we gave the Storyboard-created Bottom constraint a Priority of 750, we've told auto-layout it's allowed to break that constraint without causing conflicts.
If / when you remove the banner view, that "new" constraint will be removed with it, and the Storyboard-created constraint will again be used.

Swift: Make the background image of UITableViewController embedded in Navigation Controller fill the entire screen

I managed to create translucent and rounded UITableViewCells in a UITableViewController that is embedded inside a Navigation Controller with this line of code in viewDidLoad():
tableView.backgroundView = UIImageView(image: UIImage(named: "nightTokyo"))
But I want the background image to fill the entire phone screen. I changed the code (and only this line of code) to:
navigationController?.view = UIImageView(image: UIImage(named: "nightTokyo"))
Now the background image fills up the entire phone screen, but my table and even the iPhone's time and battery indicator icons are missing.
What I want is for the background image to fill the entire screen, but the tableView, its cells, the iPhone time, battery level icon, etc. to remain displayed.
navigationController?.setNavigationBarHidden(true, animated: true)
Here is what I did which worked for me using Swift 5, XCode 12.
Step 1 (Optional) - Create a custom UINavigationController class
class CustomNavigationController: UINavigationController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationBar.isTranslucent = true
}
Replace your UINavigationController with this UINavigationController subclass. I mark this as optional as this is based on preference, if you do not set this, your navigation bar will be opaque and you cannot see what's beneath it.
Setting the navigationBar.isTranslucent = true allows you to see the background beneath it which is what I like. A subclass is also optional but you might need to make other updates to your nav bar so I always like to make this a subclass.
Step 2 - Set up your background view constraints
class CustomViewController: UIViewController {
// your background view
let bgImageView: UIImageView = {
let bgImageView = UIImageView()
bgImageView.image = UIImage(named: "gradient_background")
bgImageView.contentMode = .scaleAspectFill
return bgImageView
}()
// Get the height of the nav bar and the status bar so you
// know how far up your background needs to go
var topBarHeight: CGFloat {
var top = self.navigationController?.navigationBar.frame.height ?? 0.0
if #available(iOS 13.0, *) {
top += UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
top += UIApplication.shared.statusBarFrame.height
}
return top
}
var isLayoutConfigured = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = "Site Visit"
// you only want to do this once
if !isLayoutConfigured() {
isLayoutConfigured = true
configBackground()
}
}
private func configBackground() {
view.addSubview(bgImageView)
configureBackgroundConstraints()
}
// Set up your constraints, main one here is the top constraint
private func configureBackgroundConstraints() {
bgImageView.translatesAutoresizingMaskIntoConstraints = false
bgImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: -topBarHeight).isActive = true
bgImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
constant: 0).isActive = true
bgImageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: 0).isActive = true
bgImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
constant: 0).isActive = true
view.layoutIfNeeded()
}
Before setting constraints:
After setting above constraints:

Snapkit centerY constraint centers item above the center Y axis

I'm trying to make a custom UICollectionView cell class. The cell consists of a content view and a label. I want the label to be in the center of the view, horizontally and vertically, but instead the label is placed above the content view's center y axis.
I've made sure that the constraints are set, no other constraints are being set, and that the issue affects all views in the content view (I added another view and set its center Y axis as a test, and that also didn't work). I also set the content view and the label's background colors to be contrasting, and have confirmed that the label is not lying on the content view's center y anchor.
Here is how I set the consraints:
label.snp.makeConstraints{make in
make.centerX.centerY.equalToSuperview()
}
Here is what I get instead. Clearly the label is not centered vertically. You can see the blue UIView, which I added as a test, is also not centered vertically.
I used to add my constraints programmatically in this way
self.view.addSubview(image)
image.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
image.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
image.heightAnchor.constraint(equalToConstant: 30).isActive = true
image.widthAnchor.constraint(equalToConstant: 30).isActive = true
and my image is declarated in this way
let image: UIImageView = {
let theImageView = UIImageView()
theImageView.image = UIImage(named: "ico_return")
theImageView.translatesAutoresizingMaskIntoConstraints = false
return theImageView
}()
Hope it helps
Can you try Following Code.
class FilterCollectionViewCell: UICollectionViewCell {
let labelTemp = UILabel()
override func awakeFromNib() {
labelTemp.backgroundColor = .white
labelTemp.textColor = .black
labelTemp.text = "testing"
self.contentView.addSubview(labelTemp)
labelTemp.snp.makeConstraints { (make) in
make.centerX.centerY.equalTo(self.contentView)
}
}
}
Fast and easy:
myLabel.snp.makeConstraints { (make) in
make.center.equalTo(self.topView.snp.center)
}

TableView Content behind TabBar

I've searched through S/OF and can't find a fix for the TableView being behind my TabBar.
I set up my TableView like this;
func setUpTableView() {
messagesTableView.frame = view.frame
messagesTableView.backgroundColor = .white
view.addSubview(messagesTableView)
messagesTableView.tableFooterView = UIView()
messagesTableView.delegate = self
messagesTableView.dataSource = self
messagesTableView.register(UITableViewCell.self, forCellReuseIdentifier: messagesCellIdentifier)
edgesForExtendedLayout = []
extendedLayoutIncludesOpaqueBars = false
messagesTableView.contentInsetAdjustmentBehavior = .never
}
Then I setUpTableView() in viewDidLoad().
According to all sources
edgesForExtendedLayout = []
extendedLayoutIncludesOpaqueBars = false
messagesTableView.contentInsetAdjustmentBehavior = .never
Should satisfy the content insets and not allow the TableView to scroll behind the TabBar.
Please note my TabBars' translucency is set to false inside TabBarController.
tabBar.isTranslucent = false
As always any help appreciated.
Adding Illustration
When scrolling to the bottom is not showing the complete TableView content as the TabBar covers the last few indexes.
Did you try this in viewDidAppear ?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
Edit
Remove
messagesTableView.frame = view.frame
and add autoLayout to your messagesTableView
messagesTableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
messagesTableView.topAnchor.constraint(equalTo: topAnchor),
messagesTableView.leftAnchor.constraint(equalTo: leftAnchor),
messagesTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
messagesTableView.rightAnchor.constraint(equalTo: rightAnchor)
])