I currently have a UIScrollView with a UIImageView (top image) positioned below the UINavigationBar. However, I want to position the UIImageView at the very top of the screen (bottom image). Is there a way to implement this?
What I've tried so far: I added a UIScrollView extension (source) that is supposed to scroll down to the view parameter provided, but it hasn't worked for me.
extension UIScrollView {
// Scroll to a specific view so that it's top is at the top our scrollview
func scrollToView(view:UIView, animated: Bool) {
if let origin = view.superview {
// Get the Y position of your child view
let childStartPoint = origin.convert(view.frame.origin, to: self)
// Scroll to a rectangle starting at the Y of your subview, with a height of the scrollview
self.scrollRectToVisible(CGRect(x:0, y:childStartPoint.y,width: 1,height: self.frame.height), animated: animated)
}
}
// Bonus: Scroll to top
func scrollToTop(animated: Bool) {
let topOffset = CGPoint(x: 0, y: -contentInset.top)
setContentOffset(topOffset, animated: animated)
}
// Bonus: Scroll to bottom
func scrollToBottom() {
let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
if(bottomOffset.y > 0) {
setContentOffset(bottomOffset, animated: true)
}
}
}
class MealDetailsVC: UIViewController {
private var mealInfo: MealInfo
init(mealInfo: MealInfo) {
self.mealInfo = mealInfo
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
scrollView.scrollToView(view: iv, animated: false) // used extension from above
}
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
return scrollView
}()
lazy var iv: UIImageView = {
let iv = UIImageView()
iv.image = Image.defaultMealImage!
iv.contentMode = .scaleAspectFill
return iv
}()
}
extension MealDetailsVC {
func setupViews() {
addBackButton()
addSubviews()
autoLayoutViews()
constrainSubviews()
}
fileprivate func addBackButton() {
...
}
#objc func goBack(sender: UIBarButtonItem) {
...
}
fileprivate func addSubviews() {
view.addSubview(scrollView)
scrollView.addSubview(iv)
}
fileprivate func autoLayoutViews() {
scrollView.translatesAutoresizingMaskIntoConstraints = false
iv.translatesAutoresizingMaskIntoConstraints = false
}
fileprivate func constrainSubviews() {
NSLayoutConstraint.activate([
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
NSLayoutConstraint.activate([
iv.topAnchor.constraint(equalTo: scrollView.topAnchor),
iv.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
iv.heightAnchor.constraint(equalTo: iv.widthAnchor, multiplier: 0.6)
])
}
}
This may help.
scrollView.contentInsetAdjustmentBehavior = .never
For more information,
This property specifies how the safe area insets are used to modify the content area of the scroll view. The default value of this property is UIScrollViewContentInsetAdjustmentAutomatic.
Don't set a height anchor and add a imageView centerXAnchor equal to ScrollView centerXAnchor constraint. set imageView.contentMode to .scaleAspectFit
UPDATED BELOW!
I have a UI structure with a horizontal scrollView nesting 5 tableViews – each representing a day of the week. I have added a UISwitch to add weekend to the week, so when the user switches it on, two more tableview-subviews are added to the scrollView. So far so good, but the switch change only takes effect, when I relaunch the application. Looks like ViewDidLoad() makes it happen, but nothing else. I added a Bool variable called isWeekOn. Its state is managed from viewDidLoad:
isWeekendOn = UserDefaults.standard.bool(forKey: "switchState")
dayTableViews = fiveOrSevenDayTableViews()
where fiveOrSevenTableViews() is a closure returning the array of tableviews with the proper count and dayTableViews is my local array variable.
lazy var fiveOrSevenDayTableViews: () -> [DayTableView] = {
if self.isWeekendOn == false {
return [self.mondayTableView, self.tuesdayTableview, self.wednesdayTableview, self.thursdayTableView, self.fridayTableView]
} else {
return [self.mondayTableView, self.tuesdayTableview, self.wednesdayTableview, self.thursdayTableView, self.fridayTableView, self.saturdayTableView,self.sundayTableView]
}
}
I added a didSet property observer to isWeekendOn and that also calls setupViews(), where the number of tableviews is also decided by calling fiveOrSevenTableViews closure .
var isWeekendOn: Bool = false {
didSet {
print("LessonVC IsWeekendon: ",isWeekendOn)
dayTableViews = fiveOrSevenDayTableViews()
setupViews()
print("didset daytableviews", fiveOrSevenDayTableViews().count)
}
}
Where my setupViews() looks like:
func setupViews() {
setupScrollView()
let numberOfTableViews = CGFloat(dayTableViews.count)
let stackView = UIStackView(arrangedSubviews: fiveOrSevenDayTableViews())
print("setupViews stacview subviews count", stackView.arrangedSubviews.count)
stackView.axis = .horizontal
stackView.distribution = .fillEqually
scrollView.addSubview(stackView)
setupStackViewConstraints(stackView, numberOfTableViews)
}
And setupScrollView():
private func setupScrollView() {
let numberOfTableViews = CGFloat(dayTableViews.count)
print("setupScrollview dableviews", numberOfTableViews)
scrollView.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height:0)
scrollView.contentSize = CGSize(width: view.frame.width * numberOfTableViews, height: 0)
view.addSubview(scrollView)
setupScrollviewConstraints()
}
All the print statements are called properly, so I am wondering, why the changes actually do not take effect real time, and instead working only relaunch.
What I tried:
As #maniponken suggested, i made a function which looks like:
func readdStackView(_ stackView: UIStackView) { stackView.removeFromSuperview()
setupViews() }
than I call this within the isWeekendOn didSet observer. Didn't work out unfortunately.
UPDATE:
Actually when I put anything in my isWeekendon didSet observer, doesn't work! For example changing my navigationBar backgroundColor...etc Everything is reflecting on console though, in the print statements! Those functions also take effect at relaunch only.I have no idea what I am doing wrong.
UPDATE2:
Removing the tables works without problem with a local UIButton! My Problem is the following though: I have a settings view controller, which has a switch for setting 5 or 7 table views. Realtime update does not work with that switch, only with le local button, triggering an #objc func. I still need that settings panel for the user though!
Try this, it's not a stackview but it works for adding (and removing) tableviews to a ViewController.
This method is not using Storyboards
In your viewcontroller, containing the tableview
import Foundation
import UIKit
class SevenTableviews: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView1: UITableView = {
let tv = UITableView()
tv.backgroundColor = .white
tv.separatorStyle = .none
return tv
}()
let tableView2: UITableView = {
let tv = UITableView()
tv.backgroundColor = .white
tv.separatorStyle = .none
return tv
}()
let tableSwitch: UISwitch = {
let switchBtn = UISwitch()
switchBtn.addTarget(self, action: #selector(switchTables), for: .touchUpInside)
return switchBtn
}()
var isTableTwoShowing = false
let reuseIdentifier = "DaysCell"
var days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
var weekendDays = ["saturday", "sunday"]
override func viewDidLoad() {
super.viewDidLoad()
setupTableview()
}
func setupTableview() {
tableView1.dataSource = self
tableView1.delegate = self
tableView1.register(DaysTableviewCell.self, forCellReuseIdentifier: reuseIdentifier)
view.addSubview(tableView1)
tableView1.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.centerYAnchor, right: view.rightAnchor)
if isTableTwoShowing == true {
tableView2.dataSource = self
tableView2.delegate = self
tableView2.register(DaysTableviewCell.self, forCellReuseIdentifier: reuseIdentifier)
view.addSubview(tableView2)
tableView2.anchor(top: view.centerYAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
}
view.addSubview(tableSwitch)
tableSwitch.anchor(bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor, paddingBottom: 24, paddingRight: 12)
}
#objc func switchTables() {
if tableSwitch.isOn {
isTableTwoShowing = true
setupTableview()
} else {
isTableTwoShowing = false
tableView2.removeFromSuperview()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == tableView1 {
return days.count
} else if tableView == tableView2 {
return weekendDays.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as! DaysTableviewCell
if tableView == tableView1 {
cell.dateLabel.text = days[indexPath.row]
return cell
} else {
cell.dateLabel.text = weekendDays[indexPath.row]
return cell
}
}
}
in your tableviewCell-class:
import Foundation
import UIKit
class DaysTableviewCell: UITableViewCell {
let identifier = "DaysCell"
let cellContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
view.backgroundColor = Colors.boxBack
view.setCellShadow()
return view
}()
let dateLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 20)
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
func setupViews() {
selectionStyle = .none
addSubview(cellContainer)
cellContainer.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 8, paddingBottom: 4, paddingRight: 8, height: 35)
cellContainer.addSubview(dateLabel)
dateLabel.anchor(top: cellContainer.topAnchor, left: cellContainer.leftAnchor, bottom: cellContainer.bottomAnchor, right: cellContainer.rightAnchor, paddingTop: 4, paddingLeft: 8, paddingBottom: 4, paddingRight: 8)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am using the same cell class for both tableviews but you can decide yourself how you want to do this.
Also, my constraints are set with an extension i found from a tutorial once:
extension UIView {
func anchor(top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, paddingTop: CGFloat? = 0, paddingLeft: CGFloat? = 0, paddingBottom: CGFloat? = 0, paddingRight: CGFloat? = 0, width: CGFloat? = nil, height: CGFloat? = nil) {
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: paddingTop!).isActive = true
}
if let left = left {
leftAnchor.constraint(equalTo: left, constant: paddingLeft!).isActive = true
}
if let bottom = bottom {
if let paddingBottom = paddingBottom {
bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
}
}
if let right = right {
if let paddingRight = paddingRight {
rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
}
if let width = width {
widthAnchor.constraint(equalToConstant: width).isActive = true
}
if let height = height {
heightAnchor.constraint(equalToConstant: height).isActive = true
}
}
}
Hope this helps
Couple notes:
You don't need to re-create / re-add your stack view ever time the switch gets changed. Add it in viewDidLoad() and then add / remove the "DayTableViews"
Use constraints for your stack view inside your scroll view, instead of calculating .contentSize.
Probably want to use an array of your "Day Tables" rather than having individual mondayTableView, tuesdayTableView, etc... vars.
Here's an example you can work from. I used a simple UIView with a centered label as a simulated "DayTableView" - should be pretty clear. Everything is via code - no #IBOutlet or #IBAction - so to test this, create a new project, add this code, and assign the startup view controller to AddToScrollViewController:
//
// AddToScrollViewController.swift
//
// Created by Don Mag on 11/15/19.
//
import UIKit
class DayTableView: UIView {
// simple UIView with a centered label
// this is just simulatig a UITableView
let theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.backgroundColor = .yellow
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
addSubview(theLabel)
NSLayoutConstraint.activate([
theLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
theLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
}
class AddToScrollViewController: UIViewController {
let theSwitch: UISwitch = {
let v = UISwitch()
return v
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.backgroundColor = .orange
return v
}()
let stackView: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 16
return v
}()
let mondayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Monday"
return v
}()
let tuesdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Tuesday"
return v
}()
let wednesdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Wednesday"
return v
}()
let thursdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Thursday"
return v
}()
let fridayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Friday"
return v
}()
let saturdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Saturday"
return v
}()
let sundayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Sunday"
return v
}()
var dayTableViews: [DayTableView] = [DayTableView]()
lazy var fiveOrSevenDayTableViews: () -> [DayTableView] = {
if self.isWeekendOn == false {
return [self.mondayTableView, self.tuesdayTableView, self.wednesdayTableView, self.thursdayTableView, self.fridayTableView]
} else {
return [self.mondayTableView, self.tuesdayTableView, self.wednesdayTableView, self.thursdayTableView, self.fridayTableView, self.saturdayTableView,self.sundayTableView]
}
}
var isWeekendOn: Bool = false {
didSet {
print("LessonVC IsWeekendon: ",isWeekendOn)
dayTableViews = fiveOrSevenDayTableViews()
setupViews()
print("didset daytableviews", fiveOrSevenDayTableViews().count)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// for each of these views...
[theSwitch, scrollView, stackView].forEach {
// we're going to use auto-layout
$0.translatesAutoresizingMaskIntoConstraints = false
}
// for each of these views...
[mondayTableView, tuesdayTableView, wednesdayTableView, thursdayTableView, fridayTableView, saturdayTableView, sundayTableView].forEach {
// we're going to use auto-layout
$0.translatesAutoresizingMaskIntoConstraints = false
// constrain widths to 160 (change to desired table view widths)
$0.widthAnchor.constraint(equalToConstant: 160.0).isActive = true
// give them a background color so we can see them
$0.backgroundColor = .systemBlue
}
// add the (empty) stack view to the scroll view
scrollView.addSubview(stackView)
// add the switch to the view
view.addSubview(theSwitch)
// add the scroll view to the view
view.addSubview(scrollView)
// use safe area for view elements
let g = view.safeAreaLayoutGuide
// we need to constrain the scroll view contents (the stack view, in this case)
// to the contentLayoutGuide so auto-layout can handle the content sizing
let sg = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// put switch in top-left corner
theSwitch.topAnchor.constraint(equalTo: g.topAnchor, constant: 12.0),
theSwitch.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 12.0),
// constrain scroll view 12-pts below the switch
// and leading / trailing / bottom at Zero
scrollView.topAnchor.constraint(equalTo: theSwitch.bottomAnchor, constant: 12.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// constrain the stack view to the scroll view's contentLayoutGuide
// with 8-pts padding on each side (easier to see the framing)
stackView.topAnchor.constraint(equalTo: sg.topAnchor, constant: 8.0),
stackView.bottomAnchor.constraint(equalTo: sg.bottomAnchor, constant: -8.0),
stackView.leadingAnchor.constraint(equalTo: sg.leadingAnchor, constant: 8.0),
stackView.trailingAnchor.constraint(equalTo: sg.trailingAnchor, constant: -8.0),
// constrain height of stack view to height of scroll view frame,
// minus 16-pts (for 8-pt padding)
stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, constant: -16),
])
// add a target for the switch
theSwitch.addTarget(self, action: #selector(switchChanged(_:)), for: .valueChanged)
// set based on saved state in UserDefaults
isWeekendOn = UserDefaults.standard.bool(forKey: "switchState")
}
#objc func switchChanged(_ sender: Any) {
// switch was tapped (toggled on/off)
if let v = sender as? UISwitch {
// update state in UserDefaults
UserDefaults.standard.set(v.isOn, forKey: "switchState")
// update the UI
isWeekendOn = v.isOn
}
}
func setupViews() {
// first, remove any existing table views
stackView.arrangedSubviews.forEach {
$0.removeFromSuperview()
}
// get the array of 5 or 7 table views
let a = fiveOrSevenDayTableViews()
// add the table views to the stack view
a.forEach {
stackView.addArrangedSubview($0)
}
print("setupViews stacview subviews count", stackView.arrangedSubviews.count)
}
}
Scrolled to the right with the "weekend switch" off:
Scrolled to the right immediately after turning the "weekend switch" on:
Edit
Here is a slightly different (bit more efficient) approach. Instead of adding / removing table views, simply show / hide the Saturday and Sunday tables. The stack view will automatically handle the scroll view's content size.
Full updated example:
//
// AddToScrollViewController.swift
//
// Created by Don Mag on 11/15/19.
//
import UIKit
class DayTableView: UIView {
// simple UIView with a centered label
// this is just simulatig a UITableView
let theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.backgroundColor = .yellow
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
addSubview(theLabel)
NSLayoutConstraint.activate([
theLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
theLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
}
class AddToScrollViewController: UIViewController {
let theSwitch: UISwitch = {
let v = UISwitch()
return v
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.backgroundColor = .orange
return v
}()
let stackView: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 16
return v
}()
let mondayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Monday"
return v
}()
let tuesdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Tuesday"
return v
}()
let wednesdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Wednesday"
return v
}()
let thursdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Thursday"
return v
}()
let fridayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Friday"
return v
}()
let saturdayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Saturday"
return v
}()
let sundayTableView: DayTableView = {
let v = DayTableView()
v.theLabel.text = "Sunday"
return v
}()
var isWeekendOn: Bool = false {
didSet {
print("LessonVC IsWeekendon: ",isWeekendOn)
setupViews()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// for each of these views...
[theSwitch, scrollView, stackView].forEach {
// we're going to use auto-layout
$0.translatesAutoresizingMaskIntoConstraints = false
}
// for each of these views...
[mondayTableView, tuesdayTableView, wednesdayTableView, thursdayTableView, fridayTableView, saturdayTableView, sundayTableView].forEach {
// we're going to use auto-layout
$0.translatesAutoresizingMaskIntoConstraints = false
// constrain widths to 160 (change to desired table view widths)
$0.widthAnchor.constraint(equalToConstant: 160.0).isActive = true
// give them a background color so we can see them
$0.backgroundColor = .systemBlue
// add them to the stack view
stackView.addArrangedSubview($0)
}
// add the stack view to the scroll view
scrollView.addSubview(stackView)
// add the switch to the view
view.addSubview(theSwitch)
// add the scroll view to the view
view.addSubview(scrollView)
// use safe area for view elements
let g = view.safeAreaLayoutGuide
// we need to constrain the scroll view contents (the stack view, in this case)
// to the contentLayoutGuide so auto-layout can handle the content sizing
let sg = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// put switch in top-left corner
theSwitch.topAnchor.constraint(equalTo: g.topAnchor, constant: 12.0),
theSwitch.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 12.0),
// constrain scroll view 12-pts below the switch
// and leading / trailing / bottom at Zero
scrollView.topAnchor.constraint(equalTo: theSwitch.bottomAnchor, constant: 12.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// constrain the stack view to the scroll view's contentLayoutGuide
// with 8-pts padding on each side (easier to see the framing)
stackView.topAnchor.constraint(equalTo: sg.topAnchor, constant: 8.0),
stackView.bottomAnchor.constraint(equalTo: sg.bottomAnchor, constant: -8.0),
stackView.leadingAnchor.constraint(equalTo: sg.leadingAnchor, constant: 8.0),
stackView.trailingAnchor.constraint(equalTo: sg.trailingAnchor, constant: -8.0),
// constrain height of stack view to height of scroll view frame,
// minus 16-pts (for 8-pt padding)
stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, constant: -16),
])
// add a target for the switch
theSwitch.addTarget(self, action: #selector(switchChanged(_:)), for: .valueChanged)
// set based on saved state in UserDefaults
isWeekendOn = UserDefaults.standard.bool(forKey: "switchState")
// update the switch UI
theSwitch.isOn = isWeekendOn
}
#objc func switchChanged(_ sender: Any) {
// switch was tapped (toggled on/off)
if let v = sender as? UISwitch {
// update state in UserDefaults
UserDefaults.standard.set(v.isOn, forKey: "switchState")
// update the UI
isWeekendOn = v.isOn
}
}
func setupViews() {
// show or hide Sat and Sun table views
saturdayTableView.isHidden = !isWeekendOn
sundayTableView.isHidden = !isWeekendOn
}
}
Why don’t you make a horizontal collection view instead of the normal scroll view. It would be easier to call reloadData whenever you want to add or delete a cell (and of course each cell is a tableView)
Finally I have solved the problem.
UPDATE: The main reason I had to set up NotificationCenter for this matter, is that I used UITabBarController to add SettingsVC to my app instead of presenting it modally. Details below.
//Skip this part for answer
My main problem – as it turned out – was that my UISwitch was on a separate vc, called SettingsViewController.
This switch supposed to do the tableview-adding and removing on my main vc. I tried with delegate protocols, targeting shared instance of settingsVC, nothing worked, but adding a local button for this – which is definitely not what I wanted.
Then I read about NotificationCenter!
I remembered it from Apples App Development For Swift book,I read last year, but forgot since.
// So the Anwswer
After I set my constraints correctly based on the great hint of #DonMag, I set up NotificationCenter for my SettingsViewController, posting to my Main VC.
class SettingsViewController: UITableViewController {
private let reuseID = "reuseId"
lazy var switchButton: UISwitch = {
let sw = UISwitch()
sw.addTarget(self, action: #selector(switchPressed), for: .valueChanged)
sw.onTintColor = AdaptiveColors.navigationBarColor
return sw
}()
static let switchNotification = Notification.Name("SettingsController.switchNotification")
var isOn = Bool() {
didSet {
NotificationCenter.default.post(name:SettingsViewController.switchNotification, object: nil)
}
}
#objc func switchPressed(_ sender: UISwitch) {
UserDefaults.standard.set(sender.isOn, forKey: "switchState")
self.isOn = sender.isOn
}
then in the mainVC:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
view.backgroundColor = .white
isWeekendOn = UserDefaults.standard.bool(forKey: "switchState")
// The Solution:
NotificationCenter.default.addObserver(self, selector: #selector(handleRefresh), name: SettingsViewController.switchNotification, object: nil)
dayTableViews = fiveOrSevenDayTableViews()
print("daytableviews count ", dayTableViews.count)
scrollView.delegate = self
editButtonItem.title = LocalizedString.edit
navigationItem.title = localizedDays[currentPage]
setupNavigationBar()
setupButtons()
setupTableViews()
setupViews()
isWeekendOn == true ? setupCurrentDayViewFor_7days() : setupCurrentDayViewFor_5days()
}
then here in mainVC's #objc func handleRefresh() { } i am handling the removal or addition!
UPDATE:
in SettingsVC:
static let switchOnNotification = Notification.Name("SettingsController.switchOnNotification")
static let switchOffNotification = Notification.Name("SettingsController.switchOffNotification")
var isOn = Bool() {
didSet {
}
willSet {
if newValue == true {
NotificationCenter.default.post(name:SettingsViewController.switchOnNotification, object: nil)
} else if newValue == false {
NotificationCenter.default.post(name:SettingsViewController.switchOffNotification, object: nil)
}
}
}
in viewDidLoad in mainVC:
NotificationCenter.default.addObserver(self, selector: #selector(handleAddWeekendTableViews), name: SettingsViewController.switchOnNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRemoveWeekendTableViews), name: SettingsViewController.switchOffNotification, object: nil)
#objc func handleAddWeekendTableViews() {
[saturdayTableView, sundayTableView].forEach {
stackView.addArrangedSubview($0)
dayTableViews.append($0)
}
}
#objc func handleRemoveWeekendTableViews() {
manageCurrentPage()
dayTableViews.removeLast(2)
[saturdayTableView, sundayTableView].forEach {
$0.removeFromSuperview()
}
}
This one is actually working!