There are a few ways you could do this -- re-calculating size on rotation (or size change) is probably not the best way.
One approach, taking advantage of UIStackView
to handle all of the layout, would be to add "blank" views to your stack view. If you set only 1 or 2 "real" views to be showing, you can then also show "blank" views to fill-out the 3 "columns."
So, assuming you might show a row with Zero real views, leave your prototype as-is, but then:
- add an IBOutlet reference to the stack view
- in
awakeFromNib()
, create and add 3 clear UIView
s to the stack view
- keep track of the real views and the blank views in arrays
Now, when you set which views to show, hide the view(s) not to show and show enough blank views to keep 3 views visible.
Examples:
[1, 2]
hide view3
and show blank1
[2]
hide view1
and view3
and show blank1
and blank2
So, you will always have 3 subviews in your stack, and no calculations are necessary... auto-layout will keep them arranged.
Here is a sample implementation:
class ThreeColCell: UITableViewCell {
@IBOutlet var mainStackView: UIStackView!
@IBOutlet var view1: UIView!
@IBOutlet var view2: UIView!
@IBOutlet var view3: UIView!
var arrayOfRealViews: [UIView] = [UIView]()
var arrayOfBlankViews: [UIView] = [UIView]()
var myData: [Int] = [Int]() {
didSet {
// hide all the views in the stack
mainStackView.arrangedSubviews.forEach {
$0.isHidden = true
}
// show the specified "real" views
myData.forEach { i in
arrayOfRealViews[i - 1].isHidden = false
}
// if fewer than 3 "real" views, show "blank" view(s)
for i in 0..<(3 - myData.count) {
arrayOfBlankViews[i].isHidden = false
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
func commonInit() -> Void {
// ordered array of views 1 to 3
arrayOfRealViews = [view1, view2, view3]
// add 3 "blank" views to the stack view
// and to array of blank views
for _ in 0..<3 {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .clear
mainStackView.addArrangedSubview(v)
arrayOfBlankViews.append(v)
}
}
}
class ThreeColTableViewController: UITableViewController {
var theData = [
[1, 2],
[1, 2, 3],
[1],
[1, 2, 3],
[2],
[2, 3],
[1, 2, 3],
[3],
[2, 3],
[1, 2, 3],
]
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ThreeColCell", for: indexPath) as! ThreeColCell
cell.myData = theData[indexPath.row]
return cell
}
}
Resulting in Portrait:

and Landscape:

Edit:
Here is another example, using an array of structs with .isDisabled1
(and 2 and 3) properties:
struct Profile {
var isDisabled1 = false
var isDisabled2 = false
var isDisabled3 = false
}
class ThreeColCell: UITableViewCell {
@IBOutlet var mainStackView: UIStackView!
@IBOutlet var view1: UIView!
@IBOutlet var view2: UIView!
@IBOutlet var view3: UIView!
var arrayOfRealViews: [UIView] = [UIView]()
var arrayOfBlankViews: [UIView] = [UIView]()
var myProfile: Profile = Profile() {
didSet {
// hide all the views in the stack
mainStackView.arrangedSubviews.forEach {
$0.isHidden = true
}
// I don't know how you have your button/label views set up, but here
// you would set button titles and label texts based on myProfile properties
// create a [1, 2, 3] array based on the .isDisabled# properties of the Profile object
var a = [Int]()
if !myProfile.isDisabled1 {
a.append(1)
}
if !myProfile.isDisabled2 {
a.append(2)
}
if !myProfile.isDisabled3 {
a.append(3)
}
// you now have an array "a" that will be
// [1, 2, 3] or
// [1, 2] or
// [2] or
// [2, 3] etc
// show the specified "real" views (arrays are Zero-based)
a.forEach { i in
arrayOfRealViews[i - 1].isHidden = false
}
// pad stackview to 3 using "blank" view(s)
// if 1 real view, show 2 blank views
// if 2 real views, show 1 blank view
// if 3 real views, don't show any blank views
for i in 0..<(3 - a.count) {
arrayOfBlankViews[i].isHidden = false
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// ordered array of views 1 to 3
arrayOfRealViews = [view1, view2, view3]
// add 3 "blank" views to the stack view
// and to array of blank views
for _ in 0..<3 {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .clear
mainStackView.addArrangedSubview(v)
arrayOfBlankViews.append(v)
}
}
}
class ThreeColTableViewController: UITableViewController {
var profilesArray: [Profile] = [Profile]()
override func viewDidLoad() {
super.viewDidLoad()
// create a few Profiles
// all 3 views are "enabled" by default
var p = Profile()
profilesArray.append(p)
// Profile with views 2 and 3 disabled
p = Profile()
p.isDisabled2 = true
p.isDisabled3 = true
profilesArray.append(p)
// Profile with view 3 disabled
p = Profile()
p.isDisabled3 = true
profilesArray.append(p)
// Profile with view 1 disabled
p = Profile()
p.isDisabled1 = true
profilesArray.append(p)
// Profile with views 1 and 2 disabled
p = Profile()
p.isDisabled1 = true
p.isDisabled2 = true
profilesArray.append(p)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profilesArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ThreeColCell", for: indexPath) as! ThreeColCell
cell.myProfile = profilesArray[indexPath.row]
return cell
}
}