Implementing Delegate Protocol - swift

I am attempting to implement a delegate protocol but I am apparently doing it all wrong. I am using a cocoapod: https://cocoapods.org/pods/SMDatePicker, which I have attempted to contact the developer for assistance but so far he's never responded to me.
After I added the pod and installed it, I added the .swift file to my project and added the following method which displays the date picker just fine:
class CalculatorViewController: UIViewController, SMDatePickerDelegate {
#IBOutlet weak var lblDate: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Initialize
let tap = UITapGestureRecognizer(target: self, action: #selector(CalculatorViewController.tapFunction))
lblDate.isUserInteractionEnabled = true
lblDate.addGestureRecognizer(tap)
// Set label date
let dt = Date()
lblDate.text = dt.asString(style: .short)
}
#IBAction func datePicker(picker: SMDatePicker, didPickDate date: NSDate) {
let picked = date as Date
lblDate.text = picked.asString(style: .short)
}
#IBAction func tapFunction(sender: UITapGestureRecognizer) {
var picker: SMDatePicker = SMDatePicker()
picker.pickerMode = .date
picker.showPickerInView(view, animated: true)
print("tap working")
}
func datePicker(_ picker: SMDatePicker, didPickDate date: Date) {
let picked = date
print(date)
}
}
I am trying to get the result from the date picker. According to the pod the various methods are as follows:
// Initialize
var picker: SMDatePicker = SMDatePicker()
// Set delegate
picker.delegate = self
You have SMDatePickerDelegate protocol to handle picker’s events. Here are list:
datePickerWillAppear(picker: SMDatePicker)
datePickerDidAppear(picker: SMDatePicker)
datePicker(picker: SMDatePicker, didPickDate date: NSDate)
datePickerDidCancel(picker: SMDatePicker)
datePickerWillDisappear(picker: SMDatePicker)
datePickerDidDisappear(picker: SMDatePicker)
My problem is I'm not sure how to implement this, any help would be greatly appreciated.

You never set the delegate of your SMDatePicker. Also, you have two renditions of didPickDate. The correct syntax in Swift is datePicker(_:didPickDate:), where the second parameter is a Date, not a NSDate.
Thus:
class ViewController: UIViewController {
#IBOutlet weak var dateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Initialize
let tap = UITapGestureRecognizer(target: self, action: #selector(didTapLabel(_:)))
dateLabel.isUserInteractionEnabled = true
dateLabel.addGestureRecognizer(tap)
// Set label date
let now = Date()
dateLabel.text = now.asString(style: .short)
}
#objc func didTapLabel(_ gesture: UITapGestureRecognizer) {
let picker = SMDatePicker()
picker.delegate = self // Don’t forget to set the delegate
picker.pickerMode = .date
picker.showPickerInView(view, animated: true)
print("tap working")
}
}
extension ViewController: SMDatePickerDelegate {
func datePicker(_ picker: SMDatePicker, didPickDate date: Date) {
dateLabel.text = date.asString(style: .short)
}
}
I personally would put the SMDatePickerDelegate methods in its own extension, just to keep the code nice and organized. Also, your tap gesture recognizer can just be declared as #objc, not #IBAction (as the latter suggests you’re hooking it up to an action in Interface Builder (hence the IB), whereas you’re just hooking it up via an Objective-C #selector pattern).

First, add an outlet to CalculatorViewController that references the date picker in your storyboard/XIB.
Then, in viewDidLoad(), assign your class as its delegate.
#IBOutlet weak var datePicker: SMDatePicker!
override func viewDidLoad() {
self.datePicker.delegate = self
}
The class that you add as its delegate must conform to the SMDatePickerDelegate protocol, which your example does.
Then, simply add the methods to your class to react to its events. For example:
func datePicker(picker: SMDatePicker, didPickDate date: NSDate) {
debugPrint("date picked! \(date)")
}
func datePickerDidCancel(picker: SMDatePicker) {
debugPrint("date picker cancelled")
}

Related

Centering a view with auto constraints - Swift

I am creating an XIB/class of datePicker which is called programatically from a calling viewController when the user taps a button. How can I use auto constraints to place this view immediately below and aligned with the centre of the calling button. My code works in portrait, but fails when the device is rotated (the datePicker does not re-center). Probably because I am passing a CGRect as an argument on init of the view, which doesn't change on rotate. I can't see any other way of overriding the passing of CGRect. When I add in auto constraints code, I get run-time auto formatting errors.
ViewController:
#IBAction func showMyDatePicker(_ sender: Any) {
showMyDatePicker.isEnabled = false
let today = Date()
let minDate = Calendar.current.date(byAdding: .month, value: -3, to: today)
let maxDate = Calendar.current.date(byAdding: .month, value: 3, to: today)
let datePickerWidth = 300
let datePickerHeight = 200
let datePickerX = Int(showMyDatePicker.center.x - CGFloat(datePickerWidth / 2))
let datePickerY = Int(showMyDatePicker.center.y + CGFloat(showMyDatePicker.bounds.height / 2))
let frame = CGRect(x: datePickerX, y: datePickerY, width: datePickerWidth, height: datePickerHeight)
myDatePicker = MyDatePicker(frame: frame)
myDatePicker?.setMyDatePicker(date: today, minimumDate: minDate!, maximumDate: maxDate!)
myDatePicker?.delegate = self
self.view.addSubview(myDatePicker!)
}
datePickerClass:
class MyDatePicker: UIView {
var delegate: MyDatePickerDelegate?
#IBOutlet var contentView: UIView!
#IBOutlet weak var datePicker: UIDatePicker!
#IBOutlet weak var returnButton: UIButton!
#IBAction func datePickerChanged(_ sender: Any) {
delegate?.dateFromDatePicker(date: datePicker.date, closeDatePickerView: false)
}
#IBAction func returnButtonTapped(_ sender: Any) {
delegate?.dateFromDatePicker(date: datePicker.date, closeDatePickerView: true)
}
override init(frame: CGRect) {
super.init(frame: frame)
initView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initView()
}
private func initView() {//QUESTION: how can I set the datepicker initial values within here
// Instantiate the view from xib file
let contentView = Bundle.main.loadNibNamed("MyDatePicker", owner: self, options: nil)?.first as? UIView
// Check that it's not nil
guard contentView != nil else {
return
}
// Add the view and set its frame
addSubview(contentView!)
contentView?.frame = self.bounds
contentView?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
func setMyDatePicker(date: Date, minimumDate: Date, maximumDate: Date) {
datePicker.date = date
datePicker.minimumDate = minimumDate
datePicker.maximumDate = maximumDate
}
}
Just add the required constraint after addSubview and setting TAMIC to false as follows:
#IBAction func showMyDatePicker(_ sender: UIButton) {
// ...
view.addSubview(myDatePicker!)
myDatePicker.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myDatePicker.topAnchor.constraint(equalTo: sender.bottomAnchor),
myDatePicker.centerXAnchor.constraint(equalTo: sender.centerXAnchor)])
}

RxSwift - make one UI element hidden/not hidden according to other element

Im using RxSwift and RxCocoa in my project.
I have some UITextField named "lastNameTF", and there is a UILabel name "lastNameTitle".
I wanna know if there is any way to set the isHidden value of lastNameTitle always be equal to isHidden value of lastNameTF using RxSwift.
I believe you can use KVO as described here -
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#kvo
It is super easy to use KVO. Here is an example of exactly what you are trying to do, just without using RxSwift (don't know what that is...)
Here is the gist of it
class ViewController: UIViewController {
private var lastNameTextFieldHiddenContext = 0
private var lastNameObservingView:UIView? = nil
#IBOutlet weak var lastNameLabel: UILabel!
#IBOutlet weak var lastNameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// add the observer
lastNameTextField.addObserver(
self,
forKeyPath: "hidden",
options: [.new],
context: &self.lastNameTextFieldHiddenContext
)
}
/// function will be called whenever an added observer is triggered
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?
) {
// make sure it is our text field isHidden observer
if context == &self.lastNameTextFieldHiddenContext {
// get the new value that was set
if let newValue = change?[NSKeyValueChangeKey.newKey] as? Bool {
// do what needs to be done when the observer is triggered
self.lastNameLabel.isHidden = newValue
}
}
}
deinit {
// remove the observer
if let view = self.lastNameObservingView {
view.removeObserver(self, forKeyPath: "hidden")
self.lastNameObservingView = nil
}
}
#IBAction func showHideButtonAction(_ sender: Any) {
self.lastNameTextField.isHidden = !self.lastNameTextField.isHidden
}
}
If you still need a simple RxSwift approach please try this:
// Controls are visible by default (isHidden = false)
let isControlHidden = BehaviorRelay<Bool>(value: false)
override func viewDidLoad() {
super.viewDidLoad()
let isHiddenDriver = self.isControlHidden.asDriver()
isHiddenDriver
.drive(self.lastNameTitle.rx.isHidden)
.disposed(by: disposeBag)
isHiddenDriver
.drive(self.lastNameTF.rx.isHidden)
.disposed(by: disposeBag)
}
Since you need both control visibilities bound to each other, you can use a Subject or Relay to achieve that, in this case isControlHidden. So, if you want to show/hide the, you just emit a new signal:
#IBAction func hide(_ sender: Any) {
self.isControlHidden.accept(true)
}
#IBAction func show(_ sender: Any) {
self.isControlHidden.accept(false)
}

Swift function textfield got focus OSX

Currently I am having multiple textfields in a view. If the user taps at one of them there should be a function responding to the event. Is there a way on how to do react (if a textfield got the focus)? I tried it with the NSTextFieldDelegate method but there is no appropriate function for this event.
This is how my code looks at the moment:
class ViewController: NSViewController, NSTextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let textField = NSTextField(frame: CGRectMake(10, 10, 37, 17))
textField.stringValue = "Label"
textField.bordered = false
textField.backgroundColor = NSColor.controlColor()
view.addSubview(textField)
textField.delegate = self
let textField2 = NSTextField(frame: CGRectMake(30, 30, 37, 17))
textField2.stringValue = "Label"
textField2.bordered = false
textField2.backgroundColor = NSColor.controlColor()
view.addSubview(textField2)
textField2.delegate = self
}
func control(control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool {
print("working") // this only works if the user enters a charakter
return true
}
}
The textShouldBeginEditing function only handles the event if the user tries to enter a character but this isn't what I want. It has to handle the event if he clicks on the textfield.
Any ideas, thanks a lot?
Edit
func myAction(sender: NSView)
{
print("aktuell: \(sender)")
currentObject = sender
}
This is the function I want to call.
1) Create a subclass of NSTextField.
import Cocoa
class MyTextField: NSTextField {
override func mouseDown(theEvent:NSEvent) {
let viewController:ViewController = ViewController()
viewController.textFieldClicked()
}
}
2) With Interface building, select the text field you want to have a focus on. Navigate to Custom Class on the right pane. Then set the class of the text field to the one you have just created.
3) The following is an example for ViewController.
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func textFieldClicked() -> Void {
print("You've clicked on me!")
}
}
4) Adding text fields programmatically...
import Cocoa
class ViewController: NSViewController {
let myField:MyTextField = MyTextField()
override func viewDidLoad() {
super.viewDidLoad()
//let myField:MyTextField = MyTextField()
myField.setFrameOrigin(NSMakePoint(20,70))
myField.setFrameSize(NSMakeSize(120,22))
let textField:NSTextField = NSTextField()
textField.setFrameOrigin(NSMakePoint(20,40))
textField.setFrameSize(NSMakeSize(120,22))
self.view.addSubview(myField)
self.view.addSubview(textField)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func textFieldClicked() -> Void {
print("You've clicked on me!")
}
}
I know it’s been answered some while ago but I did eventually find this solution for macOS in Swift 3 (it doesn’t work for Swift 4 unfortunately) which notifies when a textfield is clicked inside (and for each key stroke).
Add this delegate to your class:-
NSTextFieldDelegate
In viewDidLoad() add these:-
imputTextField.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: Notification.Name.NSTextViewDidChangeSelection, object: nil)
Then add this function:-
func textDidChange(_ notification: Notification) {
print("Its come here textDidChange")
guard (notification.object as? NSTextView) != nil else { return }
let numberOfCharatersInTextfield: Int = textFieldCell.accessibilityNumberOfCharacters()
print("numberOfCharatersInTextfield = \(numberOfCharatersInTextfield)")
}
Hope this helps others.

Swift Delegate setting a label from a custom popUp textfield

I have a custom popup view that has a UIDatePicker. This, when changed, changes the date of the save time. I also want the label on the in the CustomCell to be updated if the date has changed. I have used a delegate protocol to update the table but I cannot get this protocol to transfer the information on save. Can you help? I think I have hooked up all the correct code in the viewController class. I have tried this answer but I cannot set the delegate in the target class and there isn't a segue A Swift example of Custom Views for Data Input (custom in-app keyboard)
protocol DatePopUpViewDelegate: class {
func pastDate(date: String) // date that is chosen in picker
func isPastDateSet(isSet: Bool) // has chosen new date
}
#IBDesignable class DatePopUpView: UIView {
var delegate: DatePopUpViewDelegate?
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "DatePopUp", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
delegate?.isPastDateSet(false)
return view
}
// close popup
#IBAction func closeButtonDatePopUp(sender: AnyObject) {
if dateToSave != openTime {
if let dateToSave = dateToSave {
SaveData.changedSaveTime = dateToSave
delegate?.pastDate(dateToSave)
delegate?.isPastDateSet(true)
}
} else {
SaveData.changedSaveTime = ""
delegate?.isPastDateSet(false)
}
}
class SaveTableViewCell: UITableViewCell, DatePopUpViewDelegate {
var changeDateLabel: Bool = false
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
changeDateLabel = false
datePopUpViewControllert.delegate = self
}
// delegate functions
func pastDate(date: String) {
self.labelDate = date
print("del date \(date)")
}
func isPastDateSet(isSet: Bool) {
self.changeDateLabel = isSet
print("is set by delegate \(isSet)")
}

PopUpPicker - does not conform to protocol

I am very new to Swift and programming in general.
I am trying to add a Pop Up Picker on a textfield and when the user selects the item from the picker, they can press OK with that item displayed in the textfield and the PopUp disappear.
I have successfully implemented this with a Pop Up Date Picker as I have used this from GutHub successfully. I thought it would be easy to mimic this code for my Pop Up Picker which has proven to be more difficult than expected.
I have a sepeate XIB file which holds the View with the Picker and OK Button. I then have 2 swift files one for the PopViewController and the other for the PopPicker.
Not even sure if this code is correct but the error I am getting is that my Picker does not conform to protocol. Code is below for both files.
PopEngineViewController
import UIKit
protocol EnginePickerViewControllerDelegate : class {
func enginePickerVCDismissed(string: UITextField?)
}
class PopEngineViewController: UIViewController {
#IBOutlet weak var container: UIView!
#IBOutlet weak var enginePicker: UIPickerView!
weak var delegate : EnginePickerViewControllerDelegate?
override convenience init() {
self.init(nibName: "PopEnginePicker", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidDisappear(animated: Bool) {
self.delegate?.enginePickerVCDismissed(nil)
}
}
and PopEnginePicker
import UIKit
public class PopEnginePicker : NSObject, UIPopoverPresentationControllerDelegate, EnginePickerViewControllerDelegate {
public typealias PopEnginePickerCallback = (forTextField : UITextField)->()
var enginePickerVC : PopEngineViewController
var popover : UIPopoverPresentationController?
var textField : UITextField!
var dataChanged : PopEnginePickerCallback?
var presented = false
var offset : CGFloat = 8.0
public init(forTextField: UITextField) {
enginePickerVC = PopEngineViewController()
self.textField = forTextField
super.init()
}
public func pick(inViewController : UIViewController, dataChanged : PopEnginePickerCallback) {
if presented {
return // we are busy
}
enginePickerVC.delegate = self
enginePickerVC.modalPresentationStyle = UIModalPresentationStyle.Popover
enginePickerVC.preferredContentSize = CGSizeMake(500,208)
popover = enginePickerVC.popoverPresentationController
if let _popover = popover {
_popover.sourceView = textField
_popover.sourceRect = CGRectMake(self.offset,textField.bounds.size.height,0,0)
_popover.delegate = self
self.dataChanged = dataChanged
inViewController.presentViewController(enginePickerVC, animated: true, completion: nil)
presented = true
}
}
func adaptivePresentationStyleForPresentationController(PC: UIPresentationController!) -> UIModalPresentationStyle {
return .None
}
}
Not even sure if I am going down the complete wrong path however I want it to look like the below as I have done with the date picker as it shows in the link below:
http://coding.tabasoft.it/ios/a-simple-ios8-popdatepicker/