I am writing an app where the user controls multiple items from the home page and can switch in between them using arrow buttons or a menu. However, I would like to be able to let the user edit the names of the items through the menu, so that when the user navigates to the menu, each row has the name of the item associated with the row, and to the right of it there is a button that could pull up an alert that lets the user change the name of the item. Currently I am using an action sheet to generate the item, but I can't find a way to display multiple items on row. My code for generating the action sheet is below:
@IBAction func tapMGName(_ sender: Any) {
let actionSheetController: UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
for index in 1...5{
let addMGSelectAction: UIAlertAction = UIAlertAction(title: "Mouthguard \(index)", style: .default){action -> Void in
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let SettingsViewController : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "SettingsViewController") as UIViewController
self.present(SettingsViewController, animated: false, completion: nil)
}
actionSheetController.addAction(addMGSelectAction)
}
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .destructive, handler: { (action) -> Void in })
actionSheetController.addAction(cancelAction)
self.present(actionSheetController, animated: true, completion: nil)
}
Unfortunately you need to build the controller from scratch. While it might seem daunting, the hardest part is the presentation logic. afterwards it's just a matter of subclassing this main class per-ui use case and giving each of those subclasses their own logics on build.
Here's your best approach for custom AlertControllers: https://gist.github.com/murphman300/4a56ace35d0ccdbf3a0c923b4ec7dc96
You use a main class like the one in this gist file here, pay attention to the comments for each part.
Then when you subclass each type
//An example subclass of PopUpAlertController
class SpecificAlertControllerSubclass : PopUpAlertController {
override func set() {
super.set() //Good idea to call the super., since you might want to add basic stuff all the time, in that case you would do this in the original set() method, not here in the subclass' set() override.
//Here you add your own content.. that you instantiate in the subclass. The fact set() is being called in viewDidLoad() means you don't need to worry about calling it later on.
}
}
class TestController : UIViewController, PopUpAlertControllerDelegate {
var popup : SpecificAlertControllerSubclass?
func bringUpPopUp() {
popup = SpecificAlertControllerSubclass()
popup?.delegate = self
present(popup!, animated: true) {
print("Popup Presented")
}
}
/*
The Delegate methods you will always need to consider when using it.
*/
func popUp(controller: PopUpAlertController, didDismiss withInfo: Any?) {
if let pop = popup, controller == pop {
//checks if the delegated controller is popup..
popup = nil
let info = withInfo != nil ? String(describing: withInfo!) : "unknown"
print("Dismissed PopUp, with reason: \(info)")
}
}
func popUp(controller: PopUpAlertController, selected item: [String : Any]?) {
//Here is where you would handle the user selecting one of your options. Dismissing the popup andPresenting another controller. or if you want the popup subclass to handle the logic and the next controller, you don't call this method, but handle it in the subclass object.
}
}
So this will popup a view in the same way as UIAlertControllers. However, the look of it completely depends on you. Certain things to remember when using this kind of class:
set()
Override: ALWAYS remember that you're adding subviews to
the popUp
view, not to view
. Of course, you can rename popUp to
whatever you want..viewDidAppear
is more useful if done at
the subclass level, for the same reason as the previous point. You
can plug in separate presentation methods there.Points 3 and 4 mean that SpecificAlertControllerSubclass
becomes something like this:
class SpecificAlertControllerSubclass : PopUpAlertController {
override func set() {
super.set() //Good idea to call the super., since you might want to add basic stuff all the time, in that case you would do this in the original set() method, not in the subclasses.
//Here you add your own content..
modalPresentationStyle = //which ever one you want
modalTransitionStyle = //which ever one you want
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .allowAnimatedContent, animations: {
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
self.popUp.alpha = 1
}) { (v) in
}
}
}
Like the comment on the second delegate method explains, you need to decide which controller should handle the action logic, in my opinion, depending on if you present new controllers when those actions are pressed, or if you're just switching up the UI. In the later case, you can make the didSelect method from the PopUpAlertController
protocol
an optional method. Here's a good tutorial on delegates.
I also set popUp's alpha to 0, this is for your use case which is a center-screen fade-in effect. Of course, if you want the popup to slide in, you can change that alpha value in the set()
override. If you ALSO want a slide in animation, you just need to set the initial position of popUp in the set()
override, and animate it where you want it to appear in the viewDidAppear
override. So take from this that this approach does require you to keep track of how you change the main class' properties in your subclass, however, the customizability of it all is pretty neat when you get the hang of it.
From here, you can pretty much do whatever you want. Just remember to handle the ui transition for the popup when an action is selected so as to make sure the transition is smooth etc.
As for your desired look. I think your best way to go would be to use a UITableView
with full-length borders, or a UICollectionView
. You should delegate which ever one you decide in PopUpAlertController, interpret the selection method with their respective didSelectItemAt
methods and call the PopUpAlertControllerDelegate
's popUp(controller: PopUpAlertController, didDismiss withInfo: Any?)
. That'll allow you to set custom icons for each row.
Here's a good tutorial on UICollectionView's done programmatically : https://www.youtube.com/watch?v=3Xv1mJvwXok&list=PL0dzCUj1L5JGKdVUtA5xds1zcyzsz7HLj