I'm learning swift and trying to figure out how to add a view controller as a dependency into my Class I'm creating. Ideally I'd like to add it in the init stage so it's 'dependable'
But I can't quite figure it out - so I'm hoping someone can help me along the way. The error I'm getting is: Property 'self.homeController' not initialized at super.init call
Basically I have a Class that creates an overlay with a pop-up on it that has a UICollectionView that is clickable. When the you click the item, the menu animates out and THEN on complete fires the function from the homecontroller.
class SettingLauncher: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
private let homeController: HomeController
func showSettings(){
// window is the full device since the view is smaller due to nav
if let window = UIApplication.shared.keyWindow {
blackView.alpha = 0
blackView.backgroundColor = UIColor(white: 0, alpha: 0.6)
blackView.frame = window.frame // set RECT to the same size as device
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
let height:CGFloat = CGFloat(settings.count) * cellHeight
let y = window.frame.height - height
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: window.frame.height / 2)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0, y: y, width: window.frame.width, height: window.frame.height / 2)
}, completion: nil)
}
}
#objc func handleDismiss(){
UIView.animate(withDuration: 0.5,
animations:{
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: window.frame.height / 2)
}
}, completion: {_ in
self.blackView.removeFromSuperview()
self.homeController.showSettingsController()
})
}
override init(){
super.init()
self.homeController = HomeController()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(SettingsCell.self, forCellWithReuseIdentifier: cellID)
}
}
In the homecontroller, The menu button that you click initializes the settingsController and tells it to display:
let settingsLauncher = SettingLauncher()
#objc func handleMore(){
settingsLauncher.showSettings()
}
func showSettingsController(){
let dummyController = UIViewController()
navigationController?.pushViewController(dummyController, animated: true)
}
The error is telling you that you must have initialised the homeController property before you call super.init() which you don't do.
Change the code to this:
override init(){
self.homeController = HomeController()
super.init()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(SettingsCell.self, forCellWithReuseIdentifier: cellID)
}
Figured it out with some help from #UpholderOfTruth and #Tj3n. Posting the working code below.
class SettingLauncher: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let homeController: HomeController
...other UICollectionview functions
// on Item click insdie UIViewCollection
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
UIView.animate(withDuration: 0.5,
animations:{
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: window.frame.height / 2)
}
}, completion: {_ in
let setting = self.settings[indexPath.item]
self.blackView.removeFromSuperview()
// Call function from Injected ViewController
self.homeController.showControllerSetting(setting: setting)
})
}
init(controller: HomeController){
homeController = controller
super.init()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(SettingsCell.self, forCellWithReuseIdentifier: cellID)
}
}
Functions in my HomeController initializing the SettingsLauncher class
lazy var settingsLauncher:SettingLauncher = {
let launcher = SettingLauncher(controller: self)
return launcher
}()
#objc func handleMore(){
settingsLauncher.showSettings()
}
func showControllerSetting(setting: Setting){
let dummyController = UIViewController()
navigationController?.pushViewController(dummyController, animated: true)
}
Related
I created custom TabBar class, but I have few problems, how I can call any function from this class when for example tab is hidden? (I would like to hide my stripe when tab bar is hidden) Also when I use tabBarController?.selectedIndex = 3 the delegate didSelect is not called, how can I solve it? That is my simple code. Thanks for any help
class customTabBar: UITabBarController {
var stripe = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let cellWidth = tabBar.frame.width/5
stripe = UIView(frame: CGRect(x: 0, y: tabBar.frame.minY + 20, width: (tabBar.frame.width/5) * 0.6, height: 6))
stripe.center.x = cellWidth/2
stripe.applyGradient(colours: [UIColor.init(hexFromString: "5897ee"),UIColor.init(hexFromString: "5228d8")])
stripe.layer.cornerRadius = 2
stripe.layer.masksToBounds = true
self.view.addSubview(stripe)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let index: Int = item.tag + 1
let cellWidth = tabBar.frame.width/5
let newPostion = cellWidth * CGFloat(index)
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: {
self.stripe.center.x = newPostion - (cellWidth/2)
})
}
}
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
//Add the stripe to tabBar,so it will hidden when tabBar hidden
let cellWidth = tabBar.frame.width/5
let stripe = UIView(frame: CGRect(x: 0, y: tabBar.frame.minY + 20, width: (tabBar.frame.width/5) * 0.6, height: 6))
stripe.center.x = cellWidth/2
stripe.layer.cornerRadius = 2
stripe.layer.masksToBounds = true
self.tabBar.addSubview(stripe)
}
//use this to observe tabBarController?.selectedIndex = 3
override var selectedIndex: Int{
didSet{
//do what you want
}
}
}
extension UIViewController {
//use this to call get MyTabBarController instance and call any function
var myTabBarcontroller : MyTabBarController?{
get{
return self.tabBarController as? MyTabBarController
}
}
}
I was just looking at Context menu of Facebook and or slack and wanted to create something similar in my App.
I have tried two methods.
First method. Having a in View Table View and sliding it from bottom to create as if it is animated on to the view. But the problem with this is that The navigation controller and Tab bar controller are not hidden and a white patch is shown over the Black (Alpha 30 %).
The second method I tried was showing a new View controller over the current view controller and presenting as a Modal presentation.
let vc = CustomActionTableViewController(nibName: "CustomActionTableViewController", bundle: nil)
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: false, completion: nil)
This works okay but the method is too slow as I have to work with lot of Notifications (To send selected index to my main View and then perform action). It is painfully slow.
Could anyone help me with how I can improve the implementation so that I can get the Action sheet similar to Facebook which is smooth and very very fluid
Check this example : Bottom pop Up
Currently I am using this in my app and it's work fine.
Since you mentioned Slack, they actually have open sourced their bottom sheet implementation, PanModal.
Using UIPresentationController and UIPanGestureRecognizer
1- create BottomMenu presentation Controller which will handle the height of your View Controller and blur
class BottomMenuPresentationController: UIPresentationController {
// MARK: - Properties
var blurEffectView: UIVisualEffectView?
var tapGestureRecognizer = UITapGestureRecognizer()
private var topHeightRatio: Float
private var bottomHeightRatio: Float
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, topHeightRatio: Float, bottomHeightRatio: Float) {
let blurEffect = UIBlurEffect(style: .systemThickMaterialDark)
blurEffectView = UIVisualEffectView(effect: blurEffect)
self.topHeightRatio = topHeightRatio
self.bottomHeightRatio = bottomHeightRatio
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissController))
self.blurEffectView?.isUserInteractionEnabled = true
self.blurEffectView?.addGestureRecognizer(tapGestureRecognizer)
}
override var frameOfPresentedViewInContainerView: CGRect {
CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height * CGFloat(topHeightRatio)),
size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height * CGFloat(bottomHeightRatio)))
}
override func presentationTransitionWillBegin() {
self.blurEffectView?.alpha = 0
if let blurEffectView = blurEffectView {
self.containerView?.addSubview(blurEffectView)
}
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
self.blurEffectView?.alpha = 0.66
}, completion: { (_) in })
}
override func dismissalTransitionWillBegin() {
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
self.blurEffectView?.alpha = 0
}, completion: { (_) in
self.blurEffectView?.removeFromSuperview()
})
}
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
presentedView!.roundCorners([.topLeft, .topRight], radius: 14)
}
override func containerViewDidLayoutSubviews() {
super.containerViewDidLayoutSubviews()
presentedView?.frame = frameOfPresentedViewInContainerView
blurEffectView?.frame = containerView!.bounds
}
#objc func dismissController() {
self.presentedViewController.dismiss(animated: true, completion: nil)
}
}
2- create Your ViewController
class BottomMenuVC: UIViewController {
// MARK: - Instances
var hasSetPointOrigin = false
var pointOrigin: CGPoint?
// MARK: - Properties
let topDarkLine: UIView = {
let view = UIView()
view.backgroundColor = UIColor(hexString: "#E1E1E1")
view.layer.cornerRadius = 2
return view
}()
let cancelButn: UIButton = {
let button = UIButton(type: .custom)
button.setAttributedTitle(NSAttributedString(string: "Cancel", attributes: [NSAttributedString.Key.font: UIFont.LatoMedium(size: 17),
NSAttributedString.Key.foregroundColor: UIColor(hexString: "#515151")
]), for: .normal)
button.backgroundColor = UIColor(hexString: "#F1F3F4")
button.layer.cornerRadius = 5.0
button.addTarget(self, action: #selector(cancelButnPressed), for: .touchUpInside)
return button
}()
// MARK: - viewLifeCycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.isUserInteractionEnabled = true
setupMenuView()
}
override func viewDidLayoutSubviews() {
if !hasSetPointOrigin {
hasSetPointOrigin = true
pointOrigin = self.view.frame.origin
}
}
// MARK: - SetupView
func setupMenuView() {
self.view.addSubview(topDarkLine)
self.view.addSubview(cancelButn)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction(_:)))
view.addGestureRecognizer(panGesture)
topDarkLine.constrainHeight(constant: 4)
topDarkLine.constrainWidth(constant: view.frame.size.width * 0.10)
topDarkLine.centerXInSuperview()
topDarkLine.anchor(top: view.topAnchor, leading: nil, bottom: nil, trailing: nil, padding: .init(top: 8, left: 0, bottom: 0, right: 0))
cancelButn.anchor(top:view.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor,
padding: .init(top: 16, left: 16, bottom: 0, right: 16))
cancelButn.constrainHeight(constant: 44)
}
// MARK: - Actions
#objc func panGestureRecognizerAction(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
// Not allowing the user to drag the view upward
guard translation.y >= 0 else { return }
// setting x as 0 because we don't want users to move the frame side ways!! Only want straight up or down in the y-axis
view.frame.origin = CGPoint(x: 0, y: self.pointOrigin!.y + translation.y)
if sender.state == .ended {
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1300 {
// Velocity fast enough to dismiss the uiview
self.dismiss(animated: true, completion: nil)
} else {
// If the dragging isn’t too fast, resetting the view back to it’s original point
UIView.animate(withDuration: 0.3) {
self.view.frame.origin = self.pointOrigin ?? CGPoint(x: 0, y: 400)
}
}
}
}
#objc func cancelButnPressed() {
dismiss(animated: true, completion: nil)
}
}
3- make the viewController that contain the button that will present your menu conforms to UIViewControllerTransitioningDelegate
extension viewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
BottomMenuPresentationController(presentedViewController: presented, presenting: presenting, topHeightRatio: 0.6, bottomHeightRatio: 0.4)
}
}
4- set the transitioning delegate to self and present your custom presentation Controller
func showBottomMenu() {
let menu = BottomMenuVC()
menu.coordinator = self
menu.modalPresentationStyle = .custom
menu.transitioningDelegate = self
present(menu, animated: true, completion: nil)
}
check this PanGesture Slidable View article
I want to make SideMenu.
my code of HomeViewController:
lazy var sideMenu: SideMenuViewController = {
let menu = SideMenuViewController()
menu.stayViewObj = self
return menu
}()
Menu button's Action:
#objc func handleMenuBtn () {
sideMenu.openMenu()
}
SideMenu's viewDidLoad :
override func viewDidLoad(){
constraintLeadingPropertiesView.constant = -viewSelectProperties.frame.width
}
SideMenu's openMenu method:
func openMenu() {
debugPrint(">>>>openMenu")
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let yCoordinate: CGFloat = statusBarHeight + 44.0
guard let window = UIApplication.shared.keyWindow else { return }
view.backgroundColor = UIColor(white: 0, alpha: 0.5)
view.alpha = 0
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(closeMenu)))
view.frame = window.frame
startFrame = CGRect(x: 0, y: yCoordinate, width: 0, height: window.frame.height - yCoordinate)
view.frame = startFrame!
window.addSubview(view)
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.alpha = 1
self.view.frame = CGRect(x: 0, y: yCoordinate, width: window.frame.width - 80, height: window.frame.height - yCoordinate)
}, completion: nil)
}
Now, I am getting constraintLeadingPropertiesView=nil and all other properties of SideMenuController are also nil accepting stayViewObj.
so get crash on viewDidLoad.
how to solve this... any suggestion also helpful for me..
Thanks!
need to instantiateInitialViewController instead of create object via lazy Var.
let menu = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController() as? SideMenuViewController
I have a login screen and upon successful login, I'm passing the object details through a navigation controller to side bar menu controller (topView controller). In side bar menu, I have two options and upon switching from other view controller to top view controller, the values are removed(As per my understanding, I'm passing values from loginVC and it may not be holding those values).
As of now side bar menu transistion is working perfectly fine. But when I switch back from AnotherVC to HomeVC , it is not holding the values which are passed from LoginVC.
Can someone help me to solve this.
Below are my code snippets
StoryBoard:
Code Snippets:
On login button click: (LOGIN VC)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginSuccessIdentifier" {
if let navController = segue.destination as? UINavigationController {
if let destinController = navController.topViewController as? HomeViewController {
destinController.loggedInUser = sender as! UserDetails!
}
}
}
}
HOME VC:
var loggedInUser:UserDetails! //UserDetails class is defined separately. Contains variables like id,firstname, lastname etc. I'm displaying those values on HomeVC
override func viewDidLoad() {
super.viewDidLoad()
addSlideMenuButton()
tokenLbl.adjustsFontSizeToFitWidth = true
nameLbl.adjustsFontSizeToFitWidth = true
idLbl.adjustsFontSizeToFitWidth = true
if(loggedInUser != nil)
{
firstLbl.text = loggedInUser.token
secondLbl.text = loggedInUser.lastname
idLbl.text = String(loggedInUser.agentId)
}
}
SIDEBARMENU VC:
protocol SlideMenuDelegate {
func slideMenuItemSelectedAtIndex(_ index : Int32)
}
class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tblMenuOptions : UITableView!
#IBOutlet var btnCloseMenuOverlay : UIButton!
var arrayMenuOptions = [Dictionary<String,String>]()
var btnMenu : UIButton!
var delegate : SlideMenuDelegate?
override func viewDidLoad() {
super.viewDidLoad()
tblMenuOptions.tableFooterView = UIView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateArrayMenuOptions()
}
func updateArrayMenuOptions(){
arrayMenuOptions.append(["title":"HOME VC", "icon":"Icon1"])
arrayMenuOptions.append(["title":"ANOTHER VC", "icon":"Icon2"])
tblMenuOptions.reloadData()
}
#IBAction func onCloseMenuClick(_ button:UIButton!){
btnMenu.tag = 0
if (self.delegate != nil) {
var index = Int32(button.tag)
if(button == self.btnCloseMenuOverlay){
index = -1
}
delegate?.slideMenuItemSelectedAtIndex(index)
}
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.view.frame = CGRect(x: -UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width,height: UIScreen.main.bounds.size.height)
self.view.layoutIfNeeded()
self.view.backgroundColor = UIColor.clear
}, completion: { (finished) -> Void in
self.view.removeFromSuperview()
self.removeFromParentViewController()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellMenu")!
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutMargins = UIEdgeInsets.zero
cell.preservesSuperviewLayoutMargins = false
cell.backgroundColor = UIColor.clear
let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
let imgIcon : UIImageView = cell.contentView.viewWithTag(100) as! UIImageView
imgIcon.image = UIImage(named: arrayMenuOptions[indexPath.row]["icon"]!)
lblTitle.text = arrayMenuOptions[indexPath.row]["title"]!
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let btn = UIButton(type: UIButtonType.custom)
btn.tag = indexPath.row
self.onCloseMenuClick(btn)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayMenuOptions.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
}
BASE VC: (FOR SLIDE DELEGATE)
class BaseViewController: UIViewController, SlideMenuDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func slideMenuItemSelectedAtIndex(_ index: Int32) {
let topViewController : UIViewController = self.navigationController!.topViewController!
print("View Controller is : \(topViewController) \n", terminator: "")
switch(index){
case 0:
print("HomeVC\n", terminator: "")
self.openViewControllerBasedOnIdentifier("HomeVC")
break
case 1:
print("AnotherVC\n", terminator: "")
self.openViewControllerBasedOnIdentifier("AnotherVC")
break
default:
print("default\n", terminator: "")
}
}
func openViewControllerBasedOnIdentifier(_ strIdentifier:String){
let destViewController : UIViewController = self.storyboard!.instantiateViewController(withIdentifier: strIdentifier)
let topViewController : UIViewController = self.navigationController!.topViewController!
if (topViewController.restorationIdentifier! == destViewController.restorationIdentifier!){
print("Same VC")
} else {
self.navigationController!.pushViewController(destViewController, animated: true)
}
}
//to add slide option button
func addSlideMenuButton(){
let btnShowMenu = UIButton(type: UIButtonType.system)
btnShowMenu.setImage(self.defaultMenuImage(), for: UIControlState())
btnShowMenu.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
btnShowMenu.addTarget(self, action: #selector(BaseViewController.onSlideMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
let customBarItem = UIBarButtonItem(customView: btnShowMenu)
self.navigationItem.leftBarButtonItem = customBarItem;
}
func defaultMenuImage() -> UIImage {
var defaultMenuImage = UIImage()
UIGraphicsBeginImageContextWithOptions(CGSize(width: 30, height: 22), false, 0.0)
UIColor.black.setFill()
UIBezierPath(rect: CGRect(x: 0, y: 3, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 10, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 17, width: 30, height: 1)).fill()
UIColor.white.setFill()
UIBezierPath(rect: CGRect(x: 0, y: 4, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 11, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 18, width: 30, height: 1)).fill()
defaultMenuImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return defaultMenuImage;
}
func onSlideMenuButtonPressed(_ sender : UIButton){
if (sender.tag == 10)
{
// To Hide Menu If it already there
self.slideMenuItemSelectedAtIndex(-1);
sender.tag = 0;
let viewMenuBack : UIView = view.subviews.last!
UIView.animate(withDuration: 0.3, animations: { () -> Void in
var frameMenu : CGRect = viewMenuBack.frame
frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width
viewMenuBack.frame = frameMenu
viewMenuBack.layoutIfNeeded()
viewMenuBack.backgroundColor = UIColor.clear
}, completion: { (finished) -> Void in
viewMenuBack.removeFromSuperview()
})
return
}
sender.isEnabled = false
sender.tag = 10
let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "MenuViewController") as! MenuViewController
menuVC.btnMenu = sender
menuVC.delegate = self
self.view.addSubview(menuVC.view)
self.addChildViewController(menuVC)
menuVC.view.layoutIfNeeded()
menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);
UIView.animate(withDuration: 0.3, animations: { () -> Void in
menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);
sender.isEnabled = true
}, completion:nil)
}
}
For innocuous data that you only want to hold during a single session, you can store it as an instance property in your App Delegate. To read/write you would just create a new reference to the AppDelegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.yourProperty = "saveStuff"
If you want the data to persist, and it is not a large amount of data, try NSUserDefaults. You just have to make sure your objects conform to the NSCoder protocol.
https://developer.apple.com/reference/foundation/userdefaults
If you want the data to persist, and it is not a large amount of data, and the data should be secure, you should use the keychain services.
https://developer.apple.com/reference/security/1658642-keychain_services
I want put several UIViewController together:
leftViewController.view.frame = CGRect(x: -200, y: 0.0, width: size.width/drawerSize, height: size.height)
// Center Drawer
centerViewController.view.frame = CGRect(x: leftViewController.view.frame.width, y: 0.0, width: centerWidth, height: size.height)
// Right Drawer
rightViewController.view.frame = CGRect(x: centerViewController.view.frame.origin.x + centerViewController.view.frame.size.width, y: 0.0, width: size.width/drawerSize, height: size.height)
In the first line I use
leftViewController.view.frame = CGRect(x: -200, y....)
They will show correctly but I can not click buttons on leftViewController.
If
leftViewController.view.frame = CGRect(x: 0.0, y...)
then could click button but this layout is not I want.
The full code is posted by #Kevin Scardina Slide Sidebar Menu IOS 8 Swift
It now function as the picture below, And I'm trying to modify it like a slide menu bar which could hid left and right menu.
/*
To use simply instantiate NVMDrawerController as your root view in your AppDelegate, or in the
StoryBoard.
Once NVMDrawerController is instantiated, set the drawerSize of the NVMDrawerController,
and its leftViewControllerIdentifier, centerViewControllerIdentifier, and
rightViewControllerIdentifier to the Storyboard Identifier of the UIViewController
you want in the different locations.
*/
class NVMDrawerController: UIViewController {
// This is where you set the drawer size (i.e. for 1/3rd use 3.0, for 1/5 use 5.0)
var drawerSize:CGFloat = 4.0
var leftViewControllerIdentifier:String = "LeftController"
var centerViewControllerIdentifier:String = "CenterController"
var rightViewControllerIdentifier:String = "RightController"
private var _leftViewController:UIViewController?
var leftViewController:UIViewController {
get{
if let vc = _leftViewController {
return vc;
}
return UIViewController();
}
}
private var _centerViewController:UIViewController?
var centerViewController:UIViewController {
get{
if let vc = _centerViewController {
return vc;
}
return UIViewController();
}
}
private var _rightViewController:UIViewController?
var rightViewController:UIViewController {
get{
if let vc = _rightViewController {
return vc;
}
return UIViewController();
}
}
static let NVMDrawerOpenLeft = 0
static let NVMDrawerOpenRight = 1
var openSide:Int {
get{
return _openSide;
}
}
private var _openSide:Int = NVMDrawerOpenLeft
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Instantiate VC's with storyboard ID's
_leftViewController = instantiateViewControllers(leftViewControllerIdentifier)
_centerViewController = instantiateViewControllers(centerViewControllerIdentifier)
_rightViewController = instantiateViewControllers(rightViewControllerIdentifier)
// Call configDrawers() and pass the drawerSize variable.
drawDrawers(UIScreen.mainScreen().bounds.size)
self.view.addSubview(leftViewController.view)
self.view.addSubview(centerViewController.view)
self.view.addSubview(rightViewController.view)
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
// This is for beginning of transition
self.drawDrawers(size)
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
// This is for after transition has completed.
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Drawing View
func drawDrawers(size:CGSize) {
// Calculate Center View's Size
let centerWidth = (size.width/drawerSize) * (drawerSize - 1)
// Left Drawer
leftViewController.view.frame = CGRect(x: 0.0, y: 0.0, width: size.width/drawerSize, height: size.height)
// Center Drawer
centerViewController.view.frame = CGRect(x: leftViewController.view.frame.width, y: 0.0, width: centerWidth, height: size.height)
// Right Drawer
rightViewController.view.frame = CGRect(x: centerViewController.view.frame.origin.x + centerViewController.view.frame.size.width, y: 0.0, width: size.width/drawerSize, height: size.height)
//rightViewController = rc
// Capture the Swipes
let swipeRight = UISwipeGestureRecognizer(target: self, action: Selector("swipeRightAction:"))
swipeRight.direction = .Right
centerViewController.view.addGestureRecognizer(swipeRight)
let swipeLeft = UISwipeGestureRecognizer(target: self, action: Selector("swipeLeftAction:"))
swipeLeft.direction = .Left
centerViewController.view.addGestureRecognizer(swipeLeft)
if(openSide == NVMDrawerController.NVMDrawerOpenLeft){
openLeftDrawer()
}
else{
openRightDrawer()
}
}
// MARK: - Open Drawers
func openLeftDrawer() {
_openSide = NVMDrawerController.NVMDrawerOpenLeft
UIView.animateWithDuration(0.1, delay: 0, options: UIViewAnimationOptions.CurveEaseIn, animations:
{ () -> Void in
// move views here
self.view.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height)
}, completion:
{ finished in
})
}
func openRightDrawer() {
_openSide = NVMDrawerController.NVMDrawerOpenRight
UIView.animateWithDuration(0.1, delay: 0, options: UIViewAnimationOptions.CurveEaseIn, animations:
{ () -> Void in
// move views here
self.view.frame = CGRect(x: self.view.bounds.origin.x - self.leftViewController.view.bounds.size.width, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height)
}, completion:
{ finished in
})
}
// MARK: - Swipe Handling
func swipeRightAction(rec: UISwipeGestureRecognizer){
self.openLeftDrawer()
}
func swipeLeftAction(rec:UISwipeGestureRecognizer){
self.openRightDrawer()
}
// MARK: - Helpers
func instantiateViewControllers(storyboardID: String) -> UIViewController {
if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("\(storyboardID)") as? UIViewController{
return viewController;
}
return UIViewController();
}
}
When your view is outside of its superview,it can't receive any touch events.You should enumerate subviews in UIView(its superview) touchWithEvents function and make it receive the event.