I'm creating a calendar app in which one of the screens has a landscape view and a portrait view. For simplicity picture the iOS apple calendar in which the landscape view is a week view (i.e. completely different than the portrait view).
I'm getting a sense of bad code structure and potential loss of efficiency in my current code. Since I basically use the users battery and CPU for the week view concurrently with the portrait view even though not everyone uses the week view. What is the better practice in implementing a different presentation depending on device rotation?
My attempts
(I've also included a code example (below) that shows my implementation of these attempts in code.)
Two UIViewController
s that is segued and "popped" depending on conditions of device orientation in viewWillTransition()
. Although that became quickly out of hand since the method triggers in all view controller currently in memory/navigationStack, resulting in additional copies of viewControllers in the navigation stack if you swap between right landscape and left landscape.
Using one UIViewController
and two UIView
subclass that is initialized and communicating to the view controller through the delegate-protocol pattern. In which during the viewWillTransition()
I simply animate an alpha change between the two UIViews
depending on the device orientation.
(I have provided two simplification to illustrate my attempts described above, methods such as dataSource and delegate methods for UICollectionViews are not included are not included in the example below.)
Attempt 1:
class PortraitCalendar: UIViewController {
let portraitCalendarView : MonthCalendar = {
// Setup of my portrait calendar, it is a UICollectionView subclass.
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(portraitCalendarView)
// Additional setup..
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
performSegue(withIdentifier: "toLandscapeCalendar", sender: nil)
}
}
}
class LandscapeCalendar: UIViewController {
let landscapeView : LandscapeView = {
// Setup of the landscape view, a UICollectionView subclass.
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(landscapeView)
// Additional setup..
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isPortrait {
navigationController?.popViewController(animated: true)
}
}
}
Attempt 2:
class PortraitCalendar: UIViewController, LandscapeCalendarDelegate {
let portraitCalendarView : MonthCalendar = {
// Setup of my portrait calendar, it is a UICollectionView subclass.
}
// UIView subclass with a UICollectionView within it as a week calendar.
let landscapeCalendar = LandscapeView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(portraitCalendarView)
view.addSubview(landscapeCalendar)
landscapeCalendar.alpha = 0
portraitCalendarView.alpha = 1
// Constraints and additional setup as well of course.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
navigationController?.isToolbarHidden = true
self.view.layoutIfNeeded()
landscapeCalendarDelegate?.splitCalendarViewWillAppear()
UIView.animate(withDuration: 0.1) {
self.portraitCalendarView.alpha = 0
self.landscapeCalendar.alpha = 1
}
} else {
self.portraitCalendarView.alpha = 1
self.landscapeCalendar.alpha = 0
}
}
}
Thanks for reading my question.
I'd definitely go for an option number 2.
That way you encapsulate all the logic related to the calendar, for example for adding event or displaying it, in one view controller, without the need to reimplement the sane logic somewhere else (eg other view controller with landscape mode). Having two views for a different layout modes is not THAT easy to maintain, but if that's the only way to show the difference between the modes it really is a fine solution. And it's much easier to maintain than two view controllers with the very similar logic.