Swift - Dynamically added action doesn't fire - ios

I have a function which should "toggle" a bar button item by changing between 2 images.
class Buttons {
func ToggleBarButton(button : UIBarButtonItem, name : String, location : BarButtonLocation, isEnabled : Bool, viewController : UIViewController) {
var iconName = name
if (!isEnabled) {
iconName += "EnabledIcon"
} else {
iconName += "DisabledIcon"
}
let newIcon = UIImage(named: iconName)
let newButton = UIBarButtonItem(image: newIcon, style: .Plain, target: self, action: button.action);
switch location {
case BarButtonLocation.Left:
viewController.navigationItem.leftBarButtonItem = newButton;
viewController.navigationItem.leftBarButtonItem?.tintColor = UIColor.blackColor();
case BarButtonLocation.SecondLeft:
viewController.navigationItem.leftBarButtonItems?[1] = newButton
viewController.navigationItem.leftBarButtonItems?[1].tintColor = UIColor.blackColor()
default:
return;
}
}
}
I also have a view controller class, in which there is the action of the bar button item.
class GradesViewController: UIViewController {
var isFilterEnabled = false
var isViewEnabled = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func filterButton_Pressed(sender: UIBarButtonItem) {
Buttons().ToggleBarButton(sender, name : "Filter", location: BarButtonLocation.Left, isEnabled: isFilterEnabled, viewController: self);
isFilterEnabled = !isFilterEnabled;
}
#IBAction func viewButton_Pressed(sender: UIBarButtonItem) {
Buttons().ToggleBarButton(sender, name : "View", location: BarButtonLocation.SecondLeft, isEnabled: isViewEnabled, viewController: self);
isViewEnabled = !isViewEnabled;
}
}
On first press it successfully changes the image to the enabled form, but on second press it doesn't do anything (press event doesn't even fire). I checked, and button.action is correctly identified as "filterButton_Pressed:". What's the problem, or is there an easier way to do this? Thanks for the answer in advance.

Put the break statement after each case and try.
And also remove the semi colons.

I just realized the problem was that I copied the code from the view controller to the button class, and didn't change target: self to target: viewController. But thanks for all the answers anyways...

Related

How can I reuse part of code (recognizer, toolbar) applied to a textview?

I have a class called ThemeVC which has a textview (connected with an IBoutlet) and functionalities applied to it (it has a recognizer that detects the tapped words).
My goal here is that I would like to extract that piece of functionality, and put it maybe in its own class or create a delegate so I could reuse that functionality on other textviews.
Anyone knows how?
I pasted my code below.
(HERE comments, are functions that should be called from any view controller)
import UIKit
class ThemeVC: UIViewController, UITextViewDelegate, UINavigationControllerDelegate {
#IBOutlet weak var themeTextView: UITextView!
var tB = UIBarButtonItem()
// Move away from ThemeVC ... ->
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// -> ... Move away from ThemeVC
override func viewDidLoad() {
super.viewDidLoad()
themeTextView.delegate = self
loadbuttons ()
//HERE
addTagSelectorToolBar ()
}
func loadbuttons () {
tB = UIBarButtonItem(image: UIImage(systemName: "hand.point.up.left"), style: .plain, target: self, action: #selector(getTag(sender:)))
navigationItem.rightBarButtonItems = [tB]
}
#objc func getTag(sender: AnyObject) {
themeTextView.resignFirstResponder()
//HERE
startTagSelection()
}
}
// Move away from ThemeVC ... ->
extension ThemeVC {
func startTagSelection () {
navigationController?.setToolbarHidden(false, animated: false)
tap.isEnabled = true
tB.isEnabled = false
themeTextView.isEditable = false
themeTextView.isSelectable = false
}
}
extension ThemeVC {
#objc func doneTagSelection(){
navigationController?.setToolbarHidden(true, animated: false)
tap.isEnabled = false
tB.isEnabled = true
themeTextView.isEditable = true
themeTextView.isSelectable = true
firstTimeGrouped = false
}
}
extension ThemeVC {
func addTagSelectorToolBar (){
addTappedTagRecognizer()
tap.isEnabled = false
let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTagSelection))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
toolbarItems = [spacer, done]
}
}
extension ThemeVC {
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
themeTextView.addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: themeTextView)
let position: CGPoint = CGPoint(x:location.x, y:location.y)
let tapPosition: UITextPosition? = themeTextView.closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = themeTextView.tokenizer.rangeEnclosingPosition(tapPosition!, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = themeTextView.text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
}
// ... -> Move away from ThemeVC
How to test my code:
Create a new project with a storyboard
On the left hand side rename viewcontroller with themeVC, and replace
its code with the code I gave.
On the storyboard, embed the controller in a navigation controller, on right side, change in identity inspector class from view controller to themeVC
add a textview and link it to the IBoutlet
Looking at the parts you want to move away from ThemeVC, I would have to say not everything should be moved away from ThemeVC.
For example, you marked startTagSelection as something you want to move away, but you reference the navigationController which belongs to the view controller so it should ideally not be the responsibility of your UITextView to update your UINavigationBar.
So the two ideas discussed in the comments was using SubClasses and Protocols.
Protocols was the suggestion of Ptit Xav so I will show one way that could be used, Ptit Xav could add an answer if something else was in mind.
I start with creating a protocol
// Name the protocol as you see appropriate
// I add #objc so it can be accessible from Storyboard
// This will be used to `hand over` responsibility of
// a certain action / event
#objc
protocol CustomTextViewTagDelegate: class {
func customTextViewDidStartSelection(_ textView: CustomTextView)
func customTextViewDidFinishSelection(_ textView: CustomTextView)
}
Next I subclass a UITextView to add my own customization
#IBDesignable
class CustomTextView: UITextView {
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// Name it as you wish
// #IBInspectable added for storyboard accessibility
// You could also make it an IBOutlet if your prefer
// that interaction
#IBInspectable
weak var tagDelegate: CustomTextViewTagDelegate?
func startTagSelection () {
// Remove the commented lines as this should the responsibility of
// the view controller, manage in the view controller using the delegate
// navigationController?.setToolbarHidden(false, animated: false)
// tB.isEnabled = false
tap.isEnabled = true
isEditable = false
isSelectable = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidStartSelection(self)
}
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self,
action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: self)
let position: CGPoint = CGPoint(x:location.x,
y: location.y)
let tapPosition: UITextPosition? = closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = tokenizer.rangeEnclosingPosition(tapPosition!,
with: UITextGranularity.word,
inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
#objc func doneTagSelection() {
// This is not the text view's responsibility, manage in the
// view controller using the delegate
// navigationController?.setToolbarHidden(true, animated: false)
// tB.isEnabled = true
tap.isEnabled = false
isEditable = true
isSelectable = true
firstTimeGrouped = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidFinishSelection(self)
}
}
And finally use it like so
class ThemeVC: UIViewController {
// Change UITextView to CustomTextView
#IBOutlet weak var themeTextView: CustomTextView!
var tB = UIBarButtonItem()
// If you do not set up the delegate in your
// storyboard, you need to it in your code
// call this function from didLoad or something
// if needed
private func configureTextView() {
themeTextView.tagDelegate = self
}
// All your other implementation
}
extension ThemeVC: CustomTextViewTagDelegate {
func customTextViewDidStartSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(false,
animated: false)
tB.isEnabled = false
}
func customTextViewDidFinishSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(true,
animated: false)
tB.isEnabled = true
}
}
I did not add addTagSelectorToolBar as part of the CustomTextView implementation as this is not a good candidate to be part of that module as all of its code is related to the view controller so i don't recommend making a part of the CustomTextView implementation.

Generic Global elegant way to add bar button items to any UIViewController of the project

Usually we have a predefined set of UIBarButtonItem in the project that can be used in the project and multiple times like a left menu button to open a side menu, it can be used in different UIViewControllers also a close button that dismiss the presented view controller.
The classic way is to add these buttons as needed, but this introduce a code duplication and we all want to avoid that.
My come up with an approach, but it's far from being perfect :
enum BarButtonItemType {
case menu, close, notification
}
enum BarButtonItemPosition{
case right, left
}
extension UIViewController {
func add(barButtons:[BarButtonItemType], position: BarButtonItemPosition) {
let barButtonItems = barButtons.map { rightBarButtonType -> UIBarButtonItem in
switch rightBarButtonType {
case .menu:
return UIBarButtonItem(image: UIImage(named:"menu"),
style: .plain,
target: self,
action: #selector(presentLeftMenu(_:)))
case .notification:
return UIBarButtonItem(image: UIImage(named:"notification"),
style: .plain,
target: self,
action: #selector(showNotification(_:)))
case .close:
return UIBarButtonItem(image: UIImage(named:"close"),
style: .plain,
target: self,
action: #selector(dismissController(_:)))
}
}
switch position {
case .right:
self.navigationItem.rightBarButtonItems = barButtonItems
case .left:
self.navigationItem.leftBarButtonItems = barButtonItems
}
}
// MARK: Actions
#objc fileprivate func presentLeftMenu(_ sender:AnyObject) {
self.parent?.presentLeftMenuViewController(sender)
}
#objc fileprivate func dismissController(_ sender:AnyObject) {
self.dismiss(animated: true, completion: nil)
}
#objc fileprivate func showNotification(_ sender:AnyObject) {
let notificationViewController = UINavigationController(rootViewController:NotificationViewController())
self.present(notificationViewController, animated: true, completion: nil)
}
}
and then the usage:
override func viewDidLoad() {
super.viewDidLoad()
self.add(barButtons: [.close], position: .right)
self.add(barButtons: [.menu], position: .left)
}
The limitations of my approach are:
The extension needs to know how to instantiate new view controller (case of notification for example) and what if viewController must be inited with parameters
It assumes that you only want to present a UIViewController
Not elegant.
I am sure that there is better way with Swift language and protocol oriented programming that can achieve the intended result with more flexibility, any thoughts ?
It seems that you're after having a default bar button configuration but specific (to subclass of UIViewController) bar button action implementations. You mentioned 1. "The extension needs to know how to instantiate new view controller" and your second point 2. "It assumes that you only want to present a UIViewController", thats a good sign that your extension should delegate that job to a subclass that knows what to do with those actions. Here I've done a sample implementation:
enum BarButtonItemPosition {
case right, left
}
enum BarButtonItemType {
case menu(BarButtonItemPosition)
case close(BarButtonItemPosition)
case notification(BarButtonItemPosition)
}
/// Has default implementation on UIViewControllers that conform to BarButtonActions.
protocol BarButtonItemConfiguration: class {
func addBarButtonItem(ofType type: BarButtonItemType)
}
/// Hate that we're forced to expose button targets to objc runtime :(
/// but I don't know any other way for the time being, maybe in Swift 6 :)
#objc protocol BarButtonActions {
#objc func presentLeftMenu(_ sender:AnyObject)
#objc func dismissController(_ sender:AnyObject)
#objc func showNotification(_ sender:AnyObject)
}
extension BarButtonItemConfiguration where Self: UIViewController, Self: BarButtonActions {
func addBarButtonItem(ofType type: BarButtonItemType) {
func newButton(imageName: String, position: BarButtonItemPosition, action: Selector?) {
let button = UIBarButtonItem(image: UIImage(named: imageName), style: .plain, target: self, action: action)
switch position {
case .left: self.navigationItem.leftBarButtonItem = button
case .right: self.navigationItem.rightBarButtonItem = button
}
}
switch type {
case .menu(let p): newButton(imageName: "", position: p, action: #selector(Self.presentLeftMenu(_:)))
case .notification(let p): newButton(imageName: "", position: p, action: #selector(Self.showNotification(_:)))
case .close(let p): newButton(imageName: "", position: p, action: #selector(Self.dismissController(_:)))
}
}
}
/// Conform to this in subclasses of UIViewController and implement BarButtonActions (its impl. differs from vc to vc).
protocol BarButtonConfigarable: BarButtonItemConfiguration, BarButtonActions {}
/// example
class SampleVC: UIViewController, BarButtonConfigarable {
override func viewDidLoad() {
super.viewDidLoad()
addBarButtonItem(ofType: .menu(.right))
addBarButtonItem(ofType: .menu(.left))
}
#objc func presentLeftMenu(_ sender:AnyObject) {
// TODO:
}
#objc func dismissController(_ sender:AnyObject) {
// TODO:
}
#objc func showNotification(_ sender:AnyObject) {
// TODO:
}
}

Swift - Prevent back event in UIViewController

i have a question about canceling the back event triggered from the back-button in a UIViewController. In Objective-C there was the following extension. I don't really know how to convert it to swift. What I tried to far was to override the backBarButton with my own functions but it's not working:
navigation.backBarButtonItem?.action = #selector(MyController.back)
navigation.backBarButtonItem?.target = self
I searched for something like a delegate function but I can't find anything for the backButton.
When i faced with this problem, i rewrited this extension to Swift 3
This solution keeps system back button with "<"
public protocol VCWithBackButtonHandler {
func shouldPopOnBackButton() -> Bool
}
extension UINavigationController: UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
if viewControllers.count < (navigationBar.items?.count) ?? 0 {
return true
}
var shouldPop = true
let vc = self.topViewController
if let vc = vc as? VCWithBackButtonHandler {
shouldPop = vc.shouldPopOnBackButton()
}
if shouldPop {
DispatchQueue.main.async {[weak self] in
_ = self?.popViewController(animated: true)
}
} else {
for subView in navigationBar.subviews {
if(0 < subView.alpha && subView.alpha < 1) {
UIView.animate(withDuration: 0.25, animations: {
subView.alpha = 1
})
}
}
}
return false
}
}
Usage:
class ViewController: UIViewController,VCWithBackButtonHandler{
public func shouldPopOnBackButton() -> Bool {
return false
}
}
You need to override the backBarButtonItem by using the navigationItem's leftBarButtonItem. This replaces the back button in the navigation bar, and you can specify the custom selector to call:
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .Done, target: self, action: #selector(self.backAction(sender:)))
func backAction(sender: AnyObject) {
//Your Code
}
Try this:
override func viewDidLoad {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Bordered, target: self, action: "back:")
self.navigationItem.leftBarButtonItem = newBackButton
}
func back(sender: UIBarButtonItem) {
// Perform your custom actions
// ...
// Go back to the previous ViewController
self.navigationController?.popViewControllerAnimated(true)
}

UITapGestureRecognizer not working for specific [UIView] array

I have the following piece of code. It's a third party library for a menu (named CarbonKit). When I try to select a specific segment (tab) and add a gesture recognizer, it doesn't work. Any ideas what I'm doing wrong?
To be clear, I placed a breakpoint in the handleTap, it it doesn't even enter the function.
override func viewDidLoad() {
super.viewDidLoad()
self.view.userInteractionEnabled = true
let tgr : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(OverviewFolder.handleTap(_:)))
// segment 2 (categories)
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].userInteractionEnabled = true
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].addGestureRecognizer(tgr)
}
// tap
func handleTap(gestureRecognizer : UITapGestureRecognizer){
let test = carbonTabSwipeNavigation.currentTabIndex
if test == 2 {
print("second item tapped")
}
}
If the 3rd party UISegmentedControl is like the generic one, you already have everything you need. Here's some of my code. If you are using IB, wire the control up to an IBAction instead.
let imageSegments = UISegmentedControl (items: ["Original","Stained"])
override func viewDidLoad() {
super.viewDidLoad()
imageSegments.tintColor = UIColor.yellow
imageSegments.selectedSegmentIndex = 1
imageSegments.addTarget(self, action: #selector(changeImageView), for: .valueChanged)
view.addSubview(imageSegments)
}
func changeImageView() {
switch imageSegments.selectedSegmentIndex {
case 0:
imageView.image = imgOriginal
case 1:
imageView.image = imgEdited
default:
break
}
imageView.setNeedsDisplay()
}

Unrecognized Selector for SideBar Instance

I'm asking this question because the answer I received in this question: How Do I Initialize Two Instances of NSObject in the same ViewController - Swift
brought me in this direction. Whether I subclass as NSObject or UIViewController, I still receive the unrecognized selector when I change my code to what's below.
I'm still trying to be able to create a left and right SideBar. However, I cannot even get one SideBar to load now. I receive an error [UIViewController Center]: unrecognized selector sent to instance xxxxx.
This question is different from the other unrecognized selector instance questions I've seen because I'm not dealing with a button and have no outlets since everything is done programmatically. Hence, I can't specify a subclass that links to a UIViewController in the storyboard.
I feel like once I solve the selector issue the code will work. As the code is right now, the application compiles fine. The problem is the runtime error that I receive. I can provide information from the debugger if it's necessary.
Fwiw, it seems like the runtime is recognizing that I have a center, left, and right viewcontroller from the error message.
I haven't included the RightSideBar code because I think if the left SideBar runs the RightSideBar will run when I add the solution. I want to keep the code you have to read through as brief as possible.
Last thing to note, I'm reaching all of my print statements. I actually print through to the point where it says "I should be showing the sideBar".
Here is the code for the SideBar:
//optional delegate methods that select when the sidebar opens and closes.
#objc protocol SideBarDelegate : class {
func sideBarDidSelectButtonAtIndex (itemIndex: Int)
optional func sideBarWillClose()
optional func sideBarWillOpen()
}
//this class sets up the actual sidebar.
class SideBar: UIViewController, SidebarTableViewControllerDelegate {
//width of the bar, tableview setup, and views for the sidebar
let barWidth:CGFloat = 175.0
let sideBarTableViewTopInset:CGFloat = 25.0
let sideBarContainerView:UIViewController = UIViewController()
let sideBarTableViewController:SidebarTableViewController = SidebarTableViewController()
var originView:UIViewController?
//var for dynamic effect and controlling the sidebar
var animator:UIDynamicAnimator!
weak var delegate:SideBarDelegate?
var isSideBarOpen:Bool = false
//initializer for the "SideBar" class.
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName NibNameOrNil:String!, bundle nibBundleOrNil:NSBundle!) {
super.init(nibName: nil, bundle: nil)
}
convenience init(){
self.init(nibName: nil, bundle: nil)
}
//initializer for the tableView of menu items.
init(sourceView: UIViewController, menuItems: Array<String>, menuImages: [UIImage]){
self.originView = sourceView
self.sideBarTableViewController.tableData = menuItems
self.sideBarTableViewController.imageData = menuImages
println("set initialization values")
super.init(nibName: nil, bundle: nil)
//initializing the views and animation for the menu.
setupSideBar()
animator = UIDynamicAnimator(referenceView: originView!.view)
println("finished initialization")
//swipe gesture recognition for opening the menu.
let showGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "handleSwipe:")
showGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Right
originView!.view.addGestureRecognizer(showGestureRecognizer)
let hideGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "handleSwipe:")
hideGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Left
originView!.view.addGestureRecognizer(hideGestureRecognizer)
}
override func viewDidLoad() {
println("view loaded")
}
//this function handles the direction of swipes
func handleSwipe(recognizer: UISwipeGestureRecognizer){
if recognizer.direction == UISwipeGestureRecognizerDirection.Left {
showSideBar(false)
delegate?.sideBarWillClose?()
println("closed the sideBar")
} else {
println("opened the sideBar")
showSideBar(true)
delegate?.sideBarWillOpen?()
}
}
//function for setting up the sidebar.
func setupSideBar () {
println("setup sideBar")
//setting up the frame/outline of the side bar.
sideBarContainerView.view.frame = CGRectMake(-barWidth - 1, originView!.view.frame.origin.y, barWidth, originView!.view.frame.size.height)
//setting up the color of the sidebar.
sideBarContainerView.view.backgroundColor = UIColor.clearColor()
//disables subviews from being confined to the sidebar.
sideBarContainerView.view.clipsToBounds = false
//placing the sidebar in the UIView
originView!.view.addSubview(sideBarContainerView.view)
//adding blur to the menu.
let blurView:UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.Light))
blurView.frame = sideBarContainerView.view.bounds
sideBarContainerView.view.addSubview(blurView)
//setting up controls for the sidebar
sideBarTableViewController.delegate = self
sideBarTableViewController.tableView.frame = sideBarContainerView.view.bounds
sideBarTableViewController.tableView.clipsToBounds = false
//disabling the scroll feature. Delete to keep the scroll feature.
sideBarTableViewController.tableView.scrollsToTop = false
//This will remove separators in the UITableCell. Delete to keep separators.
sideBarTableViewController.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
//This sets the background color of the sidebar and creates the inset.
sideBarTableViewController.tableView.backgroundColor = UIColor.clearColor()
sideBarTableViewController.tableView.contentInset = UIEdgeInsets(top: sideBarTableViewTopInset, left: 0, bottom: 0, right: 0)
//reloads the sidebar and adds the container view to the sideBarTableViewController.
sideBarTableViewController.tableView.reloadData()
sideBarContainerView.view.addSubview(sideBarTableViewController.tableView)
originView?.addChildViewController(sideBarContainerView)
sideBarContainerView.didMoveToParentViewController(originView)
}
func showSideBar(shouldOpen: Bool){
animator.removeAllBehaviors()
isSideBarOpen = shouldOpen
println("I should be showing the sideBar")
//simple if and else statements to define the direction of animation and intensity of animation
let gravityX:CGFloat = (shouldOpen) ? 0.5 : -0.5
let magnitude:CGFloat = (shouldOpen) ? 20 : -20
let boundaryX:CGFloat = (shouldOpen) ? barWidth : -barWidth - 1
//controls the behavior of the animation.
let gravityBehavior: UIGravityBehavior = UIGravityBehavior(items: [sideBarContainerView])
gravityBehavior.gravityDirection = CGVectorMake(gravityX, 0)
animator.addBehavior(gravityBehavior)
let collisionBehavior: UICollisionBehavior = UICollisionBehavior(items: [sideBarContainerView])
collisionBehavior.addBoundaryWithIdentifier("sideBarBoundary", fromPoint: CGPointMake(boundaryX, 20), toPoint: CGPointMake(boundaryX, originView!.view.frame.size.height))
animator.addBehavior(collisionBehavior)
let pushBehavior:UIPushBehavior = UIPushBehavior(items: [sideBarContainerView], mode: UIPushBehaviorMode.Instantaneous)
pushBehavior.magnitude = magnitude
animator.addBehavior(pushBehavior)
let sideBarBehavior:UIDynamicItemBehavior = UIDynamicItemBehavior(items: [sideBarContainerView])
sideBarBehavior.elasticity = 0.3
animator.addBehavior(sideBarBehavior)
}
func sidebarControlDidSelectRow(indexPath: NSIndexPath) {
delegate?.sideBarDidSelectButtonAtIndex(indexPath.row)
}
}
Here is the Home ViewController:
class Home: UIViewController, SideBarDelegate {
//*** Must put logout code into the logout button it should log the user out if they press it ***
var sideBar:SideBar = SideBar()
var homeImage = UIImage(named: "Shine Home")
var profileImage = UIImage(named: "Shine Profile")
var shareImage = UIImage(named: "Shine Share")
var aboutImage = UIImage(named: "Shine About")
var helpImage = UIImage(named: "Shine Help")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//setting up the menu items for the sidebar.
sideBar = SideBar(sourceView: self, menuItems: ["Home", "Profile", "Share", "About", "Help"], menuImages: [homeImage!, profileImage!, shareImage!, aboutImage!, helpImage!])
sideBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sideBarDidSelectButtonAtIndex(itemIndex: Int) {
switch itemIndex {
case 0:
let vc = storyboard?.instantiateViewControllerWithIdentifier("Home") as! Home
self.navigationController?.pushViewController(vc, animated: true)
case 1:
performSegueWithIdentifier("profile", sender: self)
case 2:
performSegueWithIdentifier("share", sender: self)
case 3:
performSegueWithIdentifier("about", sender: self)
case 4:
performSegueWithIdentifier("help", sender: self)
default:
break
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
This situation of unrecognized selector stems from the runtime not knowing what specific object you want to activate.
The runtime sees sideBarContainerView object, which is acceptable for the compiler.
The problem is that your animation behavior is for views. Your UIViewController Object has not descended into UIViews. You need to add .view to all sideBarContainerView in your showSideBar function.
If you breakdown your function and just use isSideBarOpen as the bool for your if statements in the constants and place it in the initialization of your menu then you'll get the Selector error immediately.
That's just a second way to point to fact that the error is past your last print statement "I should be showing SideBar.
If you do that then your menu will load. So far, I have no solution for getting both menus to initialize as a left and right menu.

Resources