Reputation: 3819
I have a complicated issue I'd like to explain visually and through code.
Currently, this is how my app is suppose to work visually:
I have a ViewControllerOne
class that contains a UITableView
with 9 cells. Any rows selected except rows 2, 6, and 7 will segue to ViewControllerTwo
with its own UITableView
and number of rows.
If rows 2, 6, or 7 is selected, a push segue will stack another ViewControllerOne
onto the existing ViewControllerOne
. The reason for doing this is because every row is a category, but rows 2, 6, and 7 also contains sub-categories that looks exactly like ViewControllerOne
Main-VC on the left.
Rather than creating another class that contains the exact same code as in Main-VC, I wanted to reuse the ViewControllerOne
class.
Now, if any rows in SUB-VC is selected, it will also perform a push segue to ViewControllerTwo
.
Since ViewControllerOne
and ViewControllerTwo
are embedded in a UINavigationController
, the issue I'm having is in the 5th step:
ViewControllerTwo
(as it should)MAIN-VC
, it will take me to SUB-VC
(as it should)ViewControllerTwo
(as it should)ViewControllerTwo
I have a Manager
class that handles the logic and communicates with ViewControllerOne
and ViewControllerTwo
to display the data.
import UIKit
enum SubGroupState
{
case SubGroup1, None, SubGroup2, SubGroup3
}
class Manager: NSObject
{
public var subGroupState = SubGroupState.None
public var oldSubGroupState = SubGroupState.None
public var showSubGroups = Bool()
override init()
{
super.init()
}
public func initializeGroupState(row: Int) -> UIViewController
{
if showSubGroups == false && oldSubGroupState == .None
{
switch row
{
case 2:
subGroupState = .SubGroup1
break
case 6:
subGroupState = .SubGroup2
break
case 7:
subGroupState = .SubGroup3
break
default:
subGroupState = .None
break
}
}
if (subGroupState != .None && oldSubGroupState == .None)
|| (subGroupState == .None && oldSubGroupState != .None)
{
showSubGroups = true
}
else
{
showSubGroups = false
}
return initializeGroupVC(row: row)
}
fileprivate func initializeGroupVC(row: Int) -> UIViewController
{
let storyboard = UIStoryboard(name: "Main",
bundle: nil)
if showSubGroups == true
&& subGroupState != .None
{
guard let viewControllerOne = storyboard.instantiateViewController(withIdentifier: "ViewControllerOne")
as? ViewControllerOne else {
return UIViewController()
}
viewControllerOne.manager.oldSubGroupState = muscleSubGroupState
viewControllerOne.manager.showSubGroups = showSubGroups
return viewControllerOne
}
else
{
guard let viewControllerTwo = storyboard.instantiateViewController(withIdentifier: "ViewControllerTwo")
as? ViewControllerTwo else {
return UIViewController()
}
return muscleGroupExercisesVC
}
}
}
The purpose of the states
is so I can handle displaying the different sub-categories depending on the state
of the selected cell.
I create an instance of Manager
in ViewControllerOne
when the user selects a cell:
extension ViewControllerOne: UITableViewDataSource
{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
tableView.deselectRow(at: indexPath,
animated: true)
let viewController = manager.initializeGroupState(row: indexPath.row)
self.navigationController?.pushViewController(viewController,
animated: true)
}
}
class ViewControllerOne: UIViewController
{
public var manager = Manager()
....
}
The issue is in logic handling in the function initializeGroupState
, but I've tried other different combinations and I always get the Sub-VC stacked on top of an existing Sub-VC for rows 2, 6, and 7, which obviously corresponds to the subgroup rows in Main-VC, and that's where the issue I'm having a difficult time handling the logic with.
If I am doing this the wrong way, is there a better alternative to what I'm trying to achieve without repeating code?
NOTE: My Storboard only has the Main-VC ViewControllerOne
with a segue to ViewControllerTwo
. The added Sub-VC ViewControllerOne
is there to visually see what I'm trying to do, but does not actually exist in my Storyboard.
Upvotes: 0
Views: 223
Reputation: 151
I think we can keep it simple as posible
class ViewControllerOne: UIViewController, UITableViewDelegate {
var isSubVC = false
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
if self.isSubVC {
// Push to ViewControllerTwo
} else {
// MainVC
if indexPath.row == 2 || indexPath.row == 6 || indexPath.row == 7 {
let subVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
subVC.isSubVC = true
self.navigationController?.pushViewController(subVC, animated: true)
} else {
// Push to ViewControllerTwo
}
}
}
}
Here is the code using your manager ideas for navigation without any tests.
enum ViewControllerType {
case main, subGroup1, subGroup2, subGroup3
}
class Manager {
public var currentState = ViewControllerType.main
public func initializeGroupState(row: Int) -> UIViewController {
if self.currentState == .main {
// MainVC, should push to SubVC if match condition
switch row {
case 2:
return self.makeSubViewController(state: .subGroup1)
case 6:
return self.makeSubViewController(state: .subGroup2)
case 7:
return self.makeSubViewController(state: .subGroup3)
default:
return self.makeViewControllerTwo()
}
} else {
// Current is SubVC, dont care kind of row, should push to ViewControllerTwo
return self.makeViewControllerTwo()
}
}
private func makeViewControllerTwo() -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewControllerTwo")
return vc
}
private func makeSubViewController(state: ViewControllerType) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
vc.manager.currentState = state
return vc
}
}
Upvotes: 1
Reputation: 1046
You can manage you viewcontroller and manager class as below:
class ViewControllerOne: UIViewController, UITableViewDelegate {
var isSubVC = false
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
let wireframe = WireFrame()
let nextVC = wireframe.getNextVC(forView: self)
self.navigationController?.pushViewController(nextVC, animated: true)
}
}
class WireFrame {
func getNextVC (forView view: UIViewController) -> UIViewController {
if view.isKind(of: ViewControllerOne) {
return ViewControllerTwo
} else {
// MainVC
if indexPath.row == 2 || indexPath.row == 6 || indexPath.row == 7 {
let subVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
subVC.isSubVC = true
return subVC
} else {
return ViewControllerTwo
}
}
}
}
Upvotes: 0
Reputation: 26096
In Objective-C, but the logic should be the same, that's what I would do:
CustomModel:
@property (nonatomic, strong) NSArray *subdata;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, assing) BOOL isSpecial; //of course wording should be adapted to the real logic and your needs
By default on the init of a CustomModel
object, isSpecial
is set to NO.
ViewControllers:
@property (nonatomic, strong), NSArray *tableViewDataSource;
The tableViewDataSource
of MainViewController
should be like this:
- CustomData isSpecial: NO
subdata = [CustomData11, CustomData12...]
- CustomData isSpecial: NO
subdata = [CustomData21, CustomData22...]
- CustomData isSpecial: YES
subdata = [CustomData31, CustomData32...]
- CustomData isSpecial: NO
subdata = [CustomData41, CustomData42...]
- CustomData isSpecial: NO
subdata = [CustomData51, CustomData52...]
- CustomData isSpecial: NO
subdata = [CustomData61, CustomData62...]
- CustomData isSpecial: YES
subdata = [CustomData71, CustomData72...]
- CustomData isSpecial: YES
subdata = [CustomData81, CustomData82...]
- CustomData isSpecial: NO
subdata = [CustomData91, CustomData92...]
Logic for deciding on next viewcontroller: You can use either Segue or manual instantiation/push, but I'd suggest you keep doing the same method for both of thems
CustomModel *selectedData = tableViewDataSource[indexPath.row]
if ([selectedData isSpecial])
{
//Go to SubVC, ie. another MainVC
SubViewController *nextVC = ... //segue.destinationViewController looping on itself or storyboard.instatianteViewController instantiating another MainViewController
nextVC.data = selectedData.subdata;
}
else
{
//Go VC2
ViewController *nextVC = ... //segue.destinationViewController or storyboard.instatianteViewController...
nextVC.data = selectedData.subdata;
}
Upvotes: 0
Reputation: 6662
OK! What's happening here is that you have segues connected to your tableView, and that causes issues when you also push a new view controller in your code.
From the code you've posted here, I think just removing the segues from your storyboard would solve the issue. Doing it in only code as you're already doing in tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
is perfectly fine.
You can delete segues by clicking on them and pressing backspace, view controller can float for themselves in the storyboard without being connected by segues.
Upvotes: 0