Best way to pass index value between functions in Swift - ios

New to Swift. I am populating a series of UIViews from values in an Array. I am needing to pass the individual index values across multiple functions at diffrent states of longPress.
What is the best way way of setting?
I have found many examples, but most have to do with index.row values of UITableViews.
EDIT: I have included a slimmed down version of the relevant code.
func createBlock(title: String) -> UIView {
let block = UIView()
block.frame = CGRectMake(0, 0, 100, 100)
block.translatesAutoresizingMaskIntoConstraints = false
block.backgroundColor = UIColor.blackColor()
let longPressRec = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longPressed(_:)))
block.addGestureRecognizer(longPressRec)
return block
}
func longPressed(sender: UILongPressGestureRecognizer){
switch (sender.state) {
case UIGestureRecognizerState.Began:
startDrag(sender)
case UIGestureRecognizerState.Changed:
dragOn(sender)
case UIGestureRecognizerState.Ended:
stopDrag(sender)
default:
return
}
}
func startDrag(sender: UILongPressGestureRecognizer){
}
func dragOn(sender: UILongPressGestureRecognizer){
}
func stopDrag(sender: UILongPressGestureRecognizer){
}

Set the tag of the sender.
Add an extension:
extension UILongPressGestureRecognizer {
var tag: Int! {
get {
return 0
}
set(newValue) {
self.tag = newValue
}
}
}
Try this:
longPressRec.tag = indexPath.row
And then:
func longPressed(sender: UILongPressGestureRecognizer){
let tag = sender.tag
//Do something with tag
}

Related

Creating a clickable UIImageView using an extension

I have the following Swift code.
extension UIImageView {
func enableClickablePrint() {
let imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
// HERE
}
func toggleClickablePrint() {
// HERE
}
#objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
The problem I'm running into is how to fill out the disableClickablePrint and toggleClickablePrint functions.
I'd like to be able to do something like the following.
extension UIImageView {
var imageTap: UITapGestureRecognizer?
func enableClickablePrint() {
imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
if let theImageTap = imageTap {
self.removeGestureRecognizer(theImageTap)
imageTap = nil
}
}
func toggleClickablePrint() {
if let theImageTap = imageTap {
disableClickablePrint()
} else {
enableClickablePrint()
}
}
#objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
But of course the problem is you can't store properties in extensions like I'm wanting to do.
Anyway to achieve this? I want to try to keep this as clean as possible, without resorting to fancy tricks unless absolutely required.
Would the correct thing to do to be to convert this into a subclass of UIImageView? I'd like to try to avoid that if possible just because if I want to turn this into a framework or library or something, subclasses don't integrate as nicely into interface builder and the developer would have to add the extra step of changing the class of all their image views. Which I think would be awesome to avoid if possible.
Your problem is that you need to be able to recognize the UIGestureRecognizer you added, but you can't store it in a property.
Here's a (tested) solution that subclasses UITapGestureRecognizer to make the UIGestureRecognizer identifiable and then searches self.gestureRecognizers with first(where:) to see if one has been added:
extension UIImageView {
class _CFTapGestureRecognizer : UITapGestureRecognizer { }
private var _imageTap: _CFTapGestureRecognizer? { return self.gestureRecognizers?.first(where: { $0 is _CFTapGestureRecognizer }) as? _CFTapGestureRecognizer }
func enableClickablePrint() {
// Only enable once
if _imageTap == nil {
let imageTap = _CFTapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
}
func disableClickablePrint() {
if let theImageTap = _imageTap {
self.removeGestureRecognizer(theImageTap)
}
}
func toggleClickablePrint() {
if _imageTap == nil {
enableClickablePrint()
} else {
disableClickablePrint()
}
}
#objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
I've use this extension for years and it work not only image but with any view.
extension UIView {
func addTapGuesture(target: Any, action: Selector) {
let tap = UITapGestureRecognizer(target: target, action: action)
tap.numberOfTapsRequired = 1
addGestureRecognizer(tap)
isUserInteractionEnabled = true
}
}
Usage:
imageView.addTapGuesture(target: self, action: #selector(imageTapped))
#objc func imageTapped() {
print("Tapped")
}
You could make an extension variable using a getter/setter:
extension UIImageView {
var tap: UITapGestureRecognizer {
get {
return //the created tap
}
set(value) {
print(value)
}
}
}
Since Extensions in swift can't contain stored properties, these are the solutions:
Fisrt one is the most used workaround is to use Objective_C runtime, by using objc_getAssociatedObject and objc_setAssociatedObject functions.
OK it's a nice solution, but if there is pure swift approach to do that so why not!
In your extension, define a struct with the field that you want to use, here you want for example UITapGestureRecognizer
Tip Create this struct as private You don't need anyone to access it of course.
Then define a computed property that will use this struct....
By doing this you have achieved what you need and you don't use the Objective-C at all .
Example :
extension UIImageView {
private struct TapGestureHelper{
static var tapGestureRecognizer : UITapGestureRecognizer?
}
var imageTap: UITapGestureRecognizer?{
get {
return TapGestureHelper.tapGestureRecognizer
}
set {
TapGestureHelper.tapGestureRecognizer = newValue
}
}
func enableClickablePrint() {
imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap!)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
guard let gesture = self.gestureRecognizers?.first else {
return
}
self.removeGestureRecognizer(gesture)
self.imageTap = nil
}
func toggleClickablePrint() {
if let theImageTap = imageTap {
disableClickablePrint()
} else {
enableClickablePrint()
}
}
#objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}

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()
}

Custom Cell Reorder Behavior in CollectionView

I am able to reorder my collectionView like so:
However, instead of all cells shifting horizontally, I would just like to swap with the following behavior (i.e. with less shuffling of cells):
I have been playing with the following delegate method:
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath
however, I am unsure how I can achieve custom reordering behavior.
I managed to achieve this by creating a subclass of UICollectionView and adding custom handling to interactive movement. While looking at possible hints on how to solve your issue, I've found this tutorial : http://nshint.io/blog/2015/07/16/uicollectionviews-now-have-easy-reordering/.
The most important part there was that interactive reordering can be done not only on UICollectionViewController. The relevant code looks like this :
var longPressGesture : UILongPressGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
// rest of setup
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.handleLongGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGesture)
}
func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case UIGestureRecognizerState.Began:
guard let selectedIndexPath = self.collectionView?.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {
break
}
collectionView?.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
case UIGestureRecognizerState.Changed:
collectionView?.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))
case UIGestureRecognizerState.Ended:
collectionView?.endInteractiveMovement()
default:
collectionView?.cancelInteractiveMovement()
}
}
This needs to be inside your view controller in which your collection view is placed. I don't know if this will work with UICollectionViewController, some additional tinkering may be needed. What led me to subclassing UICollectionView was realisation that all other related classes/delegate methods are informed only about the first and last index paths (i.e. the source and destination), and there is no information about all the other cells that got rearranged, so It needed to be stopped at the core.
SwappingCollectionView.swift :
import UIKit
extension UIView {
func snapshot() -> UIImage {
UIGraphicsBeginImageContext(self.bounds.size)
self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
extension CGPoint {
func distanceToPoint(p:CGPoint) -> CGFloat {
return sqrt(pow((p.x - x), 2) + pow((p.y - y), 2))
}
}
struct SwapDescription : Hashable {
var firstItem : Int
var secondItem : Int
var hashValue: Int {
get {
return (firstItem * 10) + secondItem
}
}
}
func ==(lhs: SwapDescription, rhs: SwapDescription) -> Bool {
return lhs.firstItem == rhs.firstItem && lhs.secondItem == rhs.secondItem
}
class SwappingCollectionView: UICollectionView {
var interactiveIndexPath : NSIndexPath?
var interactiveView : UIView?
var interactiveCell : UICollectionViewCell?
var swapSet : Set<SwapDescription> = Set()
var previousPoint : CGPoint?
static let distanceDelta:CGFloat = 2 // adjust as needed
override func beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) -> Bool {
self.interactiveIndexPath = indexPath
self.interactiveCell = self.cellForItemAtIndexPath(indexPath)
self.interactiveView = UIImageView(image: self.interactiveCell?.snapshot())
self.interactiveView?.frame = self.interactiveCell!.frame
self.addSubview(self.interactiveView!)
self.bringSubviewToFront(self.interactiveView!)
self.interactiveCell?.hidden = true
return true
}
override func updateInteractiveMovementTargetPosition(targetPosition: CGPoint) {
if (self.shouldSwap(targetPosition)) {
if let hoverIndexPath = self.indexPathForItemAtPoint(targetPosition), let interactiveIndexPath = self.interactiveIndexPath {
let swapDescription = SwapDescription(firstItem: interactiveIndexPath.item, secondItem: hoverIndexPath.item)
if (!self.swapSet.contains(swapDescription)) {
self.swapSet.insert(swapDescription)
self.performBatchUpdates({
self.moveItemAtIndexPath(interactiveIndexPath, toIndexPath: hoverIndexPath)
self.moveItemAtIndexPath(hoverIndexPath, toIndexPath: interactiveIndexPath)
}, completion: {(finished) in
self.swapSet.remove(swapDescription)
self.dataSource?.collectionView(self, moveItemAtIndexPath: interactiveIndexPath, toIndexPath: hoverIndexPath)
self.interactiveIndexPath = hoverIndexPath
})
}
}
}
self.interactiveView?.center = targetPosition
self.previousPoint = targetPosition
}
override func endInteractiveMovement() {
self.cleanup()
}
override func cancelInteractiveMovement() {
self.cleanup()
}
func cleanup() {
self.interactiveCell?.hidden = false
self.interactiveView?.removeFromSuperview()
self.interactiveView = nil
self.interactiveCell = nil
self.interactiveIndexPath = nil
self.previousPoint = nil
self.swapSet.removeAll()
}
func shouldSwap(newPoint: CGPoint) -> Bool {
if let previousPoint = self.previousPoint {
let distance = previousPoint.distanceToPoint(newPoint)
return distance < SwappingCollectionView.distanceDelta
}
return false
}
}
I do realize that there is a lot going on there, but I hope everything will be clear in a minute.
Extension on UIView with helper method to get a snapshot of a cell.
Extension on CGPoint with helper method to calculate distance between two points.
SwapDescription helper structure - it is needed to prevent multiple swaps of the same pair of items, which resulted in glitchy animations. Its hashValue method could be improved, but was good enough for the sake of this proof of concept.
beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) -> Bool - the method called when the movement begins. Everything gets setup here. We get a snapshot of our cell and add it as a subview - this snapshot will be what the user actually drags on screen. The cell itself gets hidden. If you return false from this method, the interactive movement will not begin.
updateInteractiveMovementTargetPosition(targetPosition: CGPoint) - method called after each user movement, which is a lot. We check if the distance from previous point is small enough to swap items - this prevents issue when the user would drag fast across screen and multiple items would get swapped with non-obvious results. If the swap can happen, we check if it is already happening, and if not we swap two items.
endInteractiveMovement(), cancelInteractiveMovement(), cleanup() - after the movement ends, we need to restore our helpers to their default state.
shouldSwap(newPoint: CGPoint) -> Bool - helper method to check if the movement was small enough so we can swap cells.
This is the result it gives :
Let me know if this is what you needed and/or if you need me to clarify something.
Here is a demo project.
Swift 5 solution of #Losiowaty solution:
var longPressGesture : UILongPressGestureRecognizer!
override func viewDidLoad()
{
super.viewDidLoad()
// rest of setup
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture))
self.collectionView?.addGestureRecognizer(longPressGesture)
}
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer)
{
switch(gesture.state)
{
case UIGestureRecognizerState.began:
guard let selectedIndexPath = self.collectionView?.indexPathForItem(at: gesture.location(in: self.collectionView)) else {
break
}
collectionView?.beginInteractiveMovementForItem(at: selectedIndexPath)
case UIGestureRecognizerState.changed:
collectionView?.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
case UIGestureRecognizerState.ended:
collectionView?.endInteractiveMovement()
default:
collectionView?.cancelInteractiveMovement()
}
}
import UIKit
extension UIView {
func snapshot() -> UIImage {
UIGraphicsBeginImageContext(self.bounds.size)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
extension CGPoint {
func distanceToPoint(p:CGPoint) -> CGFloat {
return sqrt(pow((p.x - x), 2) + pow((p.y - y), 2))
}
}
struct SwapDescription : Hashable {
var firstItem : Int
var secondItem : Int
var hashValue: Int {
get {
return (firstItem * 10) + secondItem
}
}
}
func ==(lhs: SwapDescription, rhs: SwapDescription) -> Bool {
return lhs.firstItem == rhs.firstItem && lhs.secondItem == rhs.secondItem
}
class SwappingCollectionView: UICollectionView {
var interactiveIndexPath : IndexPath?
var interactiveView : UIView?
var interactiveCell : UICollectionViewCell?
var swapSet : Set<SwapDescription> = Set()
var previousPoint : CGPoint?
static let distanceDelta:CGFloat = 2 // adjust as needed
override func beginInteractiveMovementForItem(at indexPath: IndexPath) -> Bool
{
self.interactiveIndexPath = indexPath
self.interactiveCell = self.cellForItem(at: indexPath)
self.interactiveView = UIImageView(image: self.interactiveCell?.snapshot())
self.interactiveView?.frame = self.interactiveCell!.frame
self.addSubview(self.interactiveView!)
self.bringSubviewToFront(self.interactiveView!)
self.interactiveCell?.isHidden = true
return true
}
override func updateInteractiveMovementTargetPosition(_ targetPosition: CGPoint) {
if (self.shouldSwap(newPoint: targetPosition))
{
if let hoverIndexPath = self.indexPathForItem(at: targetPosition), let interactiveIndexPath = self.interactiveIndexPath {
let swapDescription = SwapDescription(firstItem: interactiveIndexPath.item, secondItem: hoverIndexPath.item)
if (!self.swapSet.contains(swapDescription)) {
self.swapSet.insert(swapDescription)
self.performBatchUpdates({
self.moveItem(at: interactiveIndexPath as IndexPath, to: hoverIndexPath)
self.moveItem(at: hoverIndexPath, to: interactiveIndexPath)
}, completion: {(finished) in
self.swapSet.remove(swapDescription)
self.dataSource?.collectionView?(self, moveItemAt: interactiveIndexPath, to: hoverIndexPath)
self.interactiveIndexPath = hoverIndexPath
})
}
}
}
self.interactiveView?.center = targetPosition
self.previousPoint = targetPosition
}
override func endInteractiveMovement() {
self.cleanup()
}
override func cancelInteractiveMovement() {
self.cleanup()
}
func cleanup() {
self.interactiveCell?.isHidden = false
self.interactiveView?.removeFromSuperview()
self.interactiveView = nil
self.interactiveCell = nil
self.interactiveIndexPath = nil
self.previousPoint = nil
self.swapSet.removeAll()
}
func shouldSwap(newPoint: CGPoint) -> Bool {
if let previousPoint = self.previousPoint {
let distance = previousPoint.distanceToPoint(p: newPoint)
return distance < SwappingCollectionView.distanceDelta
}
return false
}
}
if you want to track numbers of cells being dragged like this behaviour:
#objc func handleLongPressGesture(_ gesture: UILongPressGestureRecognizer) {
switch gesture.state {
case .began:
guard let targetIndexPath = reorderCollectionView.indexPathForItem(at: gesture.location(in: reorderCollectionView)) else {
return
}
reorderCollectionView.beginInteractiveMovementForItem(at: targetIndexPath)
case .changed:
reorderCollectionView.updateInteractiveMovementTargetPosition(gesture.location(in: reorderCollectionView))
reorderCollectionView.performBatchUpdates {
self.reorderCollectionView.visibleCells.compactMap { $0 as? ReorderCell}
.enumerated()
.forEach { (index, cell) in
cell.countLabel.text = "\(index + 1)"
}
}
case .ended:
reorderCollectionView.endInteractiveMovement()
default:
reorderCollectionView.cancelInteractiveMovement()
}
}

How to pass multiple parameters addTarget?

I need to pass two parameters. How can i pass by addTarget action. If it possible what changes i need to perform?
This is my current code.
button.tag = numbers[index];
button.addTarget(self, action: #selector(ViewController.buttonClicked(_:)), forControlEvents:UIControlEvents.TouchUpInside)
func buttonClicked(sender: UIButton){
print(sender.tag)
}
If you want more then one perimeter pass then you can use a objc_setAssociatedObject.
Any thing will be pass like Dictionary,Array,String,Int.
import ObjectiveC
extension UIButton {
private struct AssociatedKeys {
static var WithValue = "KeyValue"
}
#IBInspectable var withValue: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.WithValue) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.WithValue,
newValue as NSString?,
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN
)
}
}
}
}
You need to use above extension:-
import ObjectiveC
button.tag = numbers[index];
button.addTarget(self, action: #selector(ViewController.buttonClicked(_:)), forControlEvents:UIControlEvents.TouchUpInside)
//set velue
button.withVelue = "1,2,3,4"
func buttonClicked(sender: UIButton){
print(sender.withVelue)
}

Swift/UISwitch: how to implement a delegate/listener

In my UITableViewController I have a custom cell which contains a switcher which is the following:
import Foundation
import UIKit
class SwitchCell: UITableViewCell {
#IBOutlet weak var label : UILabel!
#IBOutlet weak var switchEmail : UISwitch!
func setEditable(canEdit:Bool) {
if (canEdit) {
self.switchEmail.enabled = true
self.label.highlighted = false
}
else {
self.switchEmail.enabled = false
self.label.highlighted = true
}
}
func configureCellWithSwitch(labelText:String, switchValue:Bool, enabled:Bool) {
var labelFrame:CGRect = self.label.frame
labelFrame.size.height = Settings.labelHeight
self.label.frame = labelFrame
self.label.text = labelText
if (switchValue) {
self.switchEmail.setOn(true, animated: true)
}
else {
self.switchEmail.setOn(false, animated: true)
}
self.setEditable(enabled)
}
}
I would like to know how to implement a listener/delegate to the switcher in order to get its value from the UITableViewController. I was able to write delegate/listeners for a cell with UITextField and UITextView implementing the methods
func controller(controller: UITableViewCell, textViewDidEndEditing: String, atIndex: Int)
and
func controller(controller: UITableViewCell, textFieldDidEndEditingWithText: String, atIndex: Int)
but I don't know what I should implement the switcher.
UISwitch has no delegate protocol. You can listen to the status as follows:
ObjC:
// somewhere in your setup:
[self.mySwitch addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
- (void)switchChanged:(UISwitch *)sender {
// Do something
BOOL value = sender.on;
}
Swift:
mySwitch.addTarget(self, action: "switchChanged:", forControlEvents: UIControlEvents.ValueChanged)
func switchChanged(mySwitch: UISwitch) {
let value = mySwitch.on
// Do something
}
Swift3 :
mySwitch.addTarget(self, action: #selector(switchChanged), for: UIControlEvents.valueChanged)
func switchChanged(mySwitch: UISwitch) {
let value = mySwitch.isOn
// Do something
}
Swift4:
mySwitch.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
#objc func switchChanged(mySwitch: UISwitch) {
let value = mySwitch.isOn
// Do something
}
In Swift4.0
mySwitch.addTarget(self, action: #selector(valueChange), for:UIControlEvents.valueChanged)
#objc func valueChange(mySwitch: UISwitch) {
let value = mySwitch.isOn
// Do something
print("switch value changed \(value)")
}
Another (Swift 3 or 4) method is to use didSet observer and drastically reduce code, like so-
In the class declaration declare a variable like below:
var switchFlag: Bool = false {
didSet{ //This will fire everytime the value for switchFlag is set
print(switchFlag) //do something with the switchFlag variable
}
}
Then you can have an IBAction on the UISwitch like so
#IBAction func switchChanged(_ sender: Any) {
if self.mySwitch.isOn{
switchFlag = true
}else{
switchFlag = false
}
}
Swift 3:
Using Storyboard Autolayout:
Add Reference:
#IBOutlet weak var sampleSwitch: UISwitch!
Associate method:
#IBAction func sampleSwitchValueChanged(_ sender: Any) {
if sampleSwitch.isOn {
print("ON")
}
else {
print ("OFF")
}
}
Programatic way:
Adding Target:
sampleSwitch.addTarget(self, action: #selector(ViewController.sampleSwitchValueChanged(sender:)), for: UIControlEvents.valueChanged)
The method associated with the switch:
func sampleSwitchValueChanged(sender: UISwitch!)
{
if sender.isOn {
print("switch on")
} else {
}
}
In Swift 5
switchDemo.addTarget(self, action: #selector(didTapAdvertise), for:UIControl.Event.valueChanged)
#objc func didTapAdvertise(mySwitch: UISwitch) {
let value = mySwitch.isOn
// Do something
print("switch value changed \(value)")
}
Swift 3:
#IBOutlet weak var mySwitch: UISwitch!
mySwitch.addTarget(self, action: #selector(MyClass.switchChanged(_:)), for: UIControlEvents.valueChanged)
func switchChanged(_ mySwitch: UISwitch) {
if mySwitch.isOn {
// handle on
} else {
// handle off
}
}
I have the solution in objective-c, it is the method that I use regularly:
-The Action of the switch must be in tableviewcontroller and not on the cell
-When You tap on the switch inside the action can do this to find the correct cell, then you can easily find the index or any other value that you need ...
- (IBAction)switchValueChanged:(UISwitch *)sender
{
YourCellClass *cell = (YourCellClass *)[sender findSuperViewWithClass:[YourCellClass class]];
etc....
}
the method findSuperviewWithClass is a category on UIView
- (UIView *)findSuperViewWithClass:(Class)superViewClass
{
UIView *superView = self.superview;
UIView *foundSuperView = nil;
while (nil != superView && nil == foundSuperView)
{
if ([superView isKindOfClass:superViewClass])
{
foundSuperView = superView;
} else
{
superView = superView.superview;
}
}
return foundSuperView;
}

Resources