Centering a view with auto constraints - Swift - 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)])
}

Related

Custom InputView Keyboard Height [duplicate]

This question already has answers here:
iOS Custom keyboard increase UIInputViewController height
(1 answer)
Unable to change UIInputView height
(4 answers)
Closed 8 months ago.
I have made a custom keyboard using a .xib and .swift file. I set it for a textfield by doing the following:
let customNumberPad = CustomNumberPad()
length.inputView = customNumberPad.inputView
However, the size of the keyboard area is large like it is using the old keyboard.
I have tried setting the height in the .xib to 200 via constraint.
I have tried:
length.inputView!.autoresizingMask = []
let heightAnch = length.inputView!.heightAnchor.constraint(equalToConstant: 200)
heightAnch.isActive = true
length.reloadInputViews()
Full CustomNumberPad code:
import UIKit
import AudioToolbox
class CustomNumberPadSmall: UIInputViewController {
#IBOutlet var numberPad: UIView!
#IBAction func insertText(_ sender: UIButton) {
if let text = sender.currentTitle {
AudioServicesPlaySystemSound (1104)
self.textDocumentProxy.insertText(text)
}
}
#IBAction func backSpace(_ sender: UIButton) {
self.textDocumentProxy.deleteBackward()
}
override func viewDidLoad() {
super.viewDidLoad()
overrideUserInterfaceStyle = USERINFO.darkModeValue
Bundle.main.loadNibNamed("CustomNumberPadSmall", owner: self)
numberPad.translatesAutoresizingMaskIntoConstraints = false
let inputView = self.inputView!
inputView.translatesAutoresizingMaskIntoConstraints = false
inputView.addSubview(numberPad)
NSLayoutConstraint.activate([
numberPad.topAnchor.constraint(equalTo: inputView.topAnchor),
numberPad.bottomAnchor.constraint(equalTo: inputView.bottomAnchor),
numberPad.leadingAnchor.constraint(equalTo: inputView.leadingAnchor),
numberPad.trailingAnchor.constraint(equalTo: inputView.trailingAnchor),
numberPad.heightAnchor.constraint(equalToConstant: 200)
])
}
}
If you only need a number pad it's better to use keyboardType property of UITextField.
yourTextField.keyboardType = .numberPad
In another case you need a custom keyboard itself, override intrinsicContentSize property of UIInputView
class CustomNumberPad: UIInputView {
// your calculated input view height
var intrinsicHeight: CGFloat = 200 {
didSet {
self.invalidateIntrinsicContentSize()
}
}
init() {
super.init(frame: CGRect(), inputViewStyle: .keyboard)
self.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.translatesAutoresizingMaskIntoConstraints = false
}
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: self.intrinsicHeight)
}
}
How to use :
let customNumberPad = CustomNumberPad()
yourTextField.inputView = customNumberPad

Implementing Delegate Protocol

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

How to add two value by using UserDefault

I'm trying to make a simple ToDo app in Swift.
I want to add not only task but also Date,
but I don't know how to add two values to a variable.
I want to add a text which get from UIDatePicker to TodoAdded.
Could you give me any advise please?
import UIKit
//Variable
var TodoAdded = [String]()
class AddController: UIViewController {
//TextField
#IBOutlet weak var TodoTextField: UITextField!
//TextField for Date
#IBOutlet weak var DateTextField: UITextField!
var datePicker: UIDatePicker = UIDatePicker()
//追加ボタンの設定
#IBAction func TodoAddButten(_ sender: Any) {
//Add typed text to variable
TodoAdded.append(TodoTextField.text!)
//Empty after tapped button
TodoTextField.text = ""
//Add to UD
UserDefaults.standard.set( TodoAdded, forKey: "TodoList" )
}
override func viewDidLoad() {
super.viewDidLoad()
// Picker Setting
datePicker.datePickerMode = UIDatePicker.Mode.dateAndTime
datePicker.timeZone = NSTimeZone.local
datePicker.locale = Locale.current
DateTextField.inputView = datePicker
//
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35))
let spacelItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
toolbar.setItems([spacelItem, doneItem], animated: true)
// InputView
DateTextField.inputView = datePicker
DateTextField.inputAccessoryView = toolbar
}
//
#objc func done() {
DateTextField.endEditing(true)
// Format
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
DateTextField.text = "\(formatter.string(from: Date()))"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You can try to save it as [String:[String]]
UserDefaults.standard.set([dateTextField.text!:todoAdded], forKey: "TodoList" )
A good approach is
struct Root:Codable {
let date:Date
let tasks:[String]
}
Then use JSONDecoder / JSONEncoder to convert to object / data , after that you can easily save / read them

UIImageView isUserInteractionEnabled = true doesn't work

The following DateField class has a date field and an icon on it. For some reason fieldIcon.isUserInteractionEnabled = true has zero affect and doesn't pass user tap to the field. Am I missing something?
import UIKit
import SnapKit
protocol DateFieldDelegate: class {
func dateSelected(_ dateField: DateField, date: Date)
}
class DateField: UIView, UITextFieldDelegate {
weak var delegate: DateFieldDelegate?
let field = UITextField()
private var datePicker: UIDatePicker?
convenience init(withStartDate startDate: Date) {
self.init()
datePicker = prepareDatePicker()
guard let datePicker = datePicker else { return }
field.inputView = datePicker
field.delegate = self
addSubview(field)
field.snp.makeConstraints { make in
make.top.equalToSuperview()
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.height.equalTo(37)
make.bottom.equalToSuperview()
}
let fieldIcon = UIImageView(image: UIImage(asset: Asset.calendar))
field.addSubview(fieldIcon)
fieldIcon.isUserInteractionEnabled = true // doesn't work
fieldIcon.snp.makeConstraints { make in
make.width.equalTo(21)
make.height.equalTo(23)
make.trailing.equalToSuperview().offset(-10)
make.centerY.equalToSuperview()
}
setStyle(.regular)
}
func textFieldDidEndEditing(_ textField: UITextField) {
guard let date = datePicker?.date else { return }
field.text = date.formatToString()
delegate?.dateSelected(self, date: date)
}
private func prepareDatePicker() -> UIDatePicker {
let datePicker = UIDatePicker()
datePicker.datePickerMode = .date
datePicker.setDate(Date(), animated: false)
datePicker.locale = Language.appLocale()
return datePicker
}
}
Not sure if I've missed something in your code but you need to add some kind of action to the image, enabling userInteractionEnabled does not tell it to recognise taps on the item and what to do when this happens. You need to add a UITapGestureRecogniser to the UIImageView
let fieldIcon = UIImageView(image: UIImage(asset: Asset.calendar))
field.addSubview(fieldIcon)
fieldIcon.isUserInteractionEnabled = true // doesn't work
let fieldIcon = UIImageView(image: UIImage(asset: Asset.calendar))
field.addSubview(fieldIcon)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(calenderIconTapped))
fieldIcon.addGestureRecognisor(tapGesture)
func calenderIconTapped() {
// show the picker
}
UPDATE:
After re-reading your question, I understand what your attempting to do now. If you want any taps on the image view to passthrough to the UITextField underneath you need to do the opposite of what you tried... you need to set userInteractionEnabled to false on the UIImageView
fieldIcon.isUserInteractionEnabled = false

incrementing points going back to 0 swift

I have added a button, that adds points to a label.
Everything works fine, and the label is then persisted into core data and appears in a tableViewCell.
When I get back to my detailsVC, I get my label with the persisted number, but when I click on the button again to increment the points, the label goes back to zero.
Here's a part of my code:
import UIKit
import CoreData
class GoalDetailsVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// IBOutlets:
#IBOutlet weak var titleTF: UITextField!
#IBOutlet weak var detailsTextView: UITextView!
#IBOutlet weak var pointsLabel: UILabel!
#IBOutlet weak var dateOfEntry: UILabel!
#IBOutlet weak var thumbImage: UIImageView!
// properties
var currentScore = 0
var goalToEdit: Goal? // goalToEdit is now an optional, and it needs to be unwrapped when used.
var imagePicker: UIImagePickerController!
override func viewDidLoad() {
super.viewDidLoad()
if let topItem = self.navigationController?.navigationBar.topItem {
topItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
}
// now we need to say that if there is a goal to edit ( not equal to nil), then we load the Goal data with the loadGoalData() function.
if goalToEdit != nil {
loadGoalData()
}
imagePicker = UIImagePickerController()
imagePicker.delegate = self
}
// when button is pressed, I need to
// 1 : add a point to the pointsLabel
// 2 : put the current date to the dateLabel
// 3 : persist the new points and date labels.
#IBAction func plusOneBtnPressed(_ sender: UIButton) {
currentScore += 1
pointsLabel.text = "\(currentScore)"
}
#IBAction func minusOneBtnPressed(_ sender: Any) {
}
#IBAction func savePressed(_ sender: Any) {
var goal: Goal!
let picture = Image(context: context) // Image = Entity
picture.image = thumbImage.image // image = attribute
if goalToEdit == nil {
goal = Goal(context: context)
} else {
goal = goalToEdit
}
goal.toImage = picture
// this is unwrapping because the original goalToEdit is an optional.
if let title = titleTF.text {
goal.title = title
}
// we saveed, or persisted the TITLE
if let points = pointsLabel.text {
goal.plusOnes = (points as NSString).intValue
}
// we saveed, or persisted the POINTS
if let details = detailsTextView.text {
goal.details = details
}
// we saved, or persisted the DETAILS
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE MMM d yyyy"
if let date = dateFormatter.date(from: dateFormatter.dateFormat) {
goal.lastEntry = date as NSDate
}
// we saved, or persisted the DATE
ad.saveContext()
_ = navigationController?.popViewController(animated: true)
}
func loadGoalData() {
if let goal = goalToEdit {
titleTF.text = goal.title
pointsLabel.text = "\(goal.plusOnes)"
detailsTextView.text = goal.details
dateOfEntry.text = (String(describing: goal.lastEntry))
thumbImage.image = goal.toImage?.image as? UIImage
}
}
When you get the persisted number you should also set currentScore to that value (if greater than 0). I believe currently you only set it to 0 that's why the incrementation starts over.