In my iOS app I have a textfield with a picker view assigned to it's inputView. On iOS devices the picker view pops up whenever the user taps the textfield. The same code does not do that, however, when I run it as a Mac/Catalyst app. My debug effort so far shows that the picker view methods are not called at all, so if the Mac requires additional textfield delegate methods to take focus away from the Mac's keyboard? Any idea how to make this work on a Mac? A barebones example code is below.
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var myTextField: UITextField!
var myPickerView : UIPickerView!
var pickerData = ["Alpha" , "Bravo" , "Charlie" , "Delta"]
override func viewDidLoad() {
super.viewDidLoad()
self.myPickerView = UIPickerView(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 300))
self.myPickerView.delegate = self
self.myPickerView.dataSource = self
self.myTextField.delegate = self
self.myTextField.inputView = self.myPickerView
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}
}
It looks like a bug in Mac Catalyst. Try using the UIPicker embedded in an UIActionSheet. ActionSheetPicker-3.0 is a ready-made solution. Show the ActionSheetPicker in textFieldDidBeginEditing of your UITextField.
ViewController
class ViewController : UIViewController {
// MARK: - Properties -
var tableView : UITableView?
var currentTextfield : UITextField?
var gendersArray : Array<String> = ["Male", "Female"]
var pickerView : UIPickerView?
// MARK: - Lifecycle -
override func viewDidLoad() {
super.viewDidLoad()
// get the size of the iOS device
let screenRect : CGRect = UIScreen.main.bounds
let screenWidth : CGFloat = screenRect.size.width
let screenHeight : CGFloat = screenRect.size.height
// add the picker view
var pickerViewFrame = CGRect(x: 0.0,
y: screenHeight - 162.0,
width: screenWidth,
height: 162.0)
// set pickerview frame for macOS Catalyst
#if targetEnvironment(macCatalyst)
pickerViewFrame = CGRect(x: 0, y: 0, width: 269, height: 240)
#endif
pickerView = UIPickerView.init(frame: pickerViewFrame)
pickerView?.delegate = self
pickerView?.dataSource = self
// ... set tableview
}
}
UIAlertController Function
// MARK: - UIAlertController -
#objc func textfieldInputAction(pickerView: UIPickerView, textfield: UITextField?) {
print("textfieldInputAction")
// alertController
let alertController = UIAlertController(title: "Title of Action Sheet", message: "", preferredStyle: UIAlertController.Style.actionSheet)
// reload components
pickerView.reloadAllComponents()
// add the picker to the alert controller
alertController.view.addSubview(pickerView)
// set height of Action sheet
let height : NSLayoutConstraint = NSLayoutConstraint(item: alertController.view!, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 350)
alertController.view.addConstraint(height)
// okAction
let okAction = UIAlertAction(title: "Done", style: .default, handler: {
(alert: UIAlertAction!) -> Void in
// end editing
DispatchQueue.main.async {
textfield?.resignFirstResponder()
DispatchQueue.main.async {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
})
alertController.addAction(okAction)
// popoverController
if let popoverController = alertController.popoverPresentationController {
popoverController.sourceView = textfield //to set the source of your alert
popoverController.sourceRect = textfield?.bounds ?? CGRect(x: 0, y: 0, width: 0, height: 0 )
popoverController.permittedArrowDirections = [.down, .up]
}
// present alertController
self.present(alertController, animated: true, completion: nil)
}
TableView
// MARK: - Tableview -
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TextFieldCell = tableView.dequeueReusableCell(withIdentifier: "TextFieldCell", for: indexPath) as! TextFieldCell
// textfield
cell.textField?.delegate = self
cell.textField?.inputView = pickerView
return cell
}
}
UITextField Delegate
// MARK: - UITextField -
extension ViewController : UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
print("textFieldShouldBeginEditing")
#if targetEnvironment(macCatalyst)
if textField.inputView == self.pickerView {
self.currentTextfield = textField
self.textfieldInputAction(pickerView: self.pickerView!, textfield: self.currentTextfield!)
return false
}
#endif
return true
}
}
UIPickerView Delegate
// MARK: - PickerView Delegates -
extension ViewController : UIPickerViewDelegate, UIPickerViewDataSource {
// components
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
// rows
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
print("numberOfRowsInComponent")
return gendersArray.count
}
// title
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gendersArray[row]
}
// select row
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print("pickerView: didSelectRow: \(row)")
currentTextfield?.text = gendersArray[row]
self.tableView?.reloadData()
}
}
This is a bit of a hack, but let me share anyway. If you listen for the NSNotification UIKeyboardWillChangeFrameNotification
this will fire before -(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
so you can set a BOOL to see if this method fires. If it doesn't, you can tell that you don't have a software keyboard, and then can show an alert controller or context menu, or however you want to handle it differently.
On UIPickerViewMioPicker.swift
class UIPickerViewMio : NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
private var uiTextField : UITextField?
private var oggetto = ["Uno", "Due", "Tre", "Quattro", "Cinque"]
required init(uiTextField : UITextField?) {
self.uiTextField = uiTextField
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 5
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
if let textField = self.uiTextField {
textField.text = self.oggetto[row]
textField.tag = row
}
}
... omissis ...}
On UIViewController
#IBOutlet var textMioText : UITextField?
var uiPickerViewMioPicker : UIPickerViewMioPicker?
override func viewDidLoad() {
... omissis ...
self.uiPickerViewMio = UIPickerViewMioPicker(uiTextField: self.textMioText)
let pickerM = UIPickerView()
pickerM.delegate = self.uiPickerViewMioPicker
pickerM.dataSource = self.uiPickerViewMioPicker
self.textMioText?.inputView = pickerM
... omissis ...
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == self.textMio {
let picker = textField.inputView as! UIPickerView
picker.isHidden = false
if UIDevice.current.systemName == "Mac OS X" {
let alertPicker = UIAlertController(title: "", message: "", preferredStyle: UIAlertController.Style.actionSheet)
alertPicker.view.addSubview(picker)
alertPicker.view.addConstraint(NSLayoutConstraint(item: alertPicker.view!, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: picker.bounds.height * 1.1))
alertPicker.view.addConstraint(NSLayoutConstraint(item: alertPicker.view!, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: picker.bounds.width * 1.1))
if let popoverController = alertPicker.popoverPresentationController {
popoverController.sourceView = textField
popoverController.sourceRect = textField.bounds
popoverController.permittedArrowDirections = [.down, .up]
}
self.present(alertPicker, animated: true, completion: nil)
return false
}
}
return true}
Related
I tried using the simulator and real phone, I still get the same error.
ERROR: Thread 1: Exception: Application tried to present a nil modal view controller on target
Here's the complete ViewController. Please see the codes below.
import UIKit
import MessageUI
class CellClass: UITableViewCell {
}
class ContactUsViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var messageField: UITextField!
#IBOutlet weak var subjectField: UIButton!
let transparentView = UIView()
let tableView = UITableView()
var selectedButton = UIButton()
var dataSource = [String]()
var MFMailComposeResultCancelled = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultFailed = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSaved = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSent = MFMailComposeResult(rawValue: 0)
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(CellClass.self, forCellReuseIdentifier: "Cell")
}
func addTransparentView(frames: CGRect) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
transparentView.frame = window?.frame ?? self.view.frame
self.view.addSubview(transparentView)
tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
self.view.addSubview(tableView)
tableView.layer.cornerRadius = 5
transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
tableView.reloadData()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeTransparentView))
transparentView.addGestureRecognizer(tapGesture)
transparentView.alpha = 0
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {self.transparentView.alpha = 0.5
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height + 5, width: frames.width, height: CGFloat(self.dataSource.count * 50))
}, completion: nil)
}
#IBAction func subjectList(_ sender: Any) {
dataSource = [" General Inquiries", " Purchase STL File"," Purchase an Eternity Band Ring", " Inquire about Measurements"]
selectedButton = subjectField
addTransparentView(frames: subjectField.frame)
}
#objc func removeTransparentView() {
let frames = selectedButton.frame
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { self.transparentView.alpha = 0
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
}, completion: nil)
}
#IBAction func sendTapped(_ sender: Any) {
let toCS = ["info#ezbandcalculator.com"]
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(toCS)
mc.setSubject(selectedButton.currentTitle!)
mc.setMessageBody("Message: \(String(describing: messageField?.text))", isHTML: false)
self.present(mc, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResultCancelled!.rawValue:
print ("Cancelled")
case MFMailComposeResultFailed!.rawValue:
print ("Failed")
case MFMailComposeResultSaved!.rawValue:
print ("Saved")
case MFMailComposeResultSent!.rawValue:
print ("Sent")
default:
break
}
controller.dismiss(animated: true, completion: nil)
}
#IBAction func dismissKeyboard(_ sender: Any) {
self.resignFirstResponder()
}
}
extension ContactUsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = dataSource[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedButton.setTitle(dataSource[indexPath.row], for: .normal)
removeTransparentView()
}
}
You may want to check whether your current device can send mail or not before presenting.
if MFMailComposeViewController.canSendMail() {
// present
}
Refer: https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller/1616879-cansendmail
I tried many different ways to create a NSTableview with custom NSTableCellView but I could not make it work. The question is, how can I do that thing?
Here is the last thing I tried:
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
cell.identifier = cellIdentifier
// array is an array that contains NSView with layers with different colors
cell.myView = array[row]
return cell
}
}
return nil
}
After adding a label:
And the full code:
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
tableview.setDelegate(self)
tableview.setDataSource(self)
let view = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view.layer?.backgroundColor = NSColor.blueColor().CGColor
array.append(view)
let view2 = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view2.layer?.backgroundColor = NSColor.greenColor().CGColor
array.append(view2)
array2label.append("bu")
array2label.append("buu")
tableview.reloadData()
// Do any additional setup after loading the view.
}
override func viewWillAppear() {
//tableview.reloadData()
laView.layer?.backgroundColor = NSColor.greenColor().CGColor
}
#IBOutlet weak var laView: NSView!
#IBOutlet weak var tableview: NSTableView!
var array = [NSView]()
var array2label = [String]()// = ["bu","buu"]
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
if (tableView.identifier == "Taula") {
return array.count
//return taulaGrafics.count
} else {
return 0
}
}
#IBOutlet weak var viewDeProva: NSView!
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
print ( "Preparem la TableView" )
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "aqui" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView = array[row]
cell.label.stringValue = array2label[row]
return cell
}
}
return nil
}
#IBAction func afegir(sender: NSButton) {
let view = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view.layer?.backgroundColor = NSColor.yellowColor().CGColor
array.append(view)
array2label.append("buLabel")
tableview.reloadData()
}
#IBAction func treure(sender: NSButton) {
array.removeLast()
tableview.reloadData()
}
}
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "aqui" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView = array[row]
cell.myView.wantsLayer = true
cell.label.stringValue = array2label[row]
return cell
}
With this changes the view show the color that is expected to be:
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
print("DidLoad")
tableview.setDelegate(self)
tableview.setDataSource(self)
}
override func viewWillAppear() {
print("WillAppear")
array.append(NSColor.blueColor().CGColor)
array2label.append("buIni")
tableview.reloadData()
laView.layer?.backgroundColor = NSColor.greenColor().CGColor
}
#IBOutlet weak var laView: NSView!
#IBOutlet weak var tableview: NSTableView!
var array = [CGColor]()
var array2label = [String]()
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
if (tableView.identifier == "Taula") {
return array.count
//return taulaGrafics.count
} else {
return 0
}
}
#IBOutlet weak var viewDeProva: NSView!
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
print ( "Preparem la TableView" )
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "Here" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView.layer?.backgroundColor = array[row]
cell.label.stringValue = array2label[row]
return cell
}
}
return nil
}
#IBAction func afegir(sender: NSButton) {
let color = NSColor.yellowColor().CGColor
array.append(color)
array2label.append("buLabel")
tableview.reloadData()
}
#IBAction func treure(sender: NSButton) {
array.removeLast()
array2label.removeLast()
tableview.reloadData()
}
}
The problem was that the NSView inside the NSTableViewCell can't be replaced by another view, because what you do doing that is changing the prototype cell. So if you replace the view the NSTableViewCell doesn't recognize that NewView layer (I'm not sure 100% why). Looks like just some information is shared.
So, how can we pass info to the tableview cell? How can we show a layer there?? Well the answer is that just modifying the prototype cell NSTableCellView added on the interface builder or its subclass (like my case, see bellow the cell). Modifying its content, but not the NSView!, inside the viewForTableColumn function. See in this piece of code:
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
cell.identifier = cellIdentifier // that is to identify the cell
cell.myView.layer?.backgroundColor = array[row] // see that the array contains CGCOLORS not NSViews
// The program was not showing the colors because the
// view copied doesn't copy its layer, or at least
// doesn't show it.
// Even though, if you say: *******---This is incorrect:
cell.myView = arrayOfNSViews[row] //the program will
// crash when removing 2 rows, myView = nil !!!!.
// That is because when you remove the item of the array
// somehow you are removing myView too, because it make a
// copy or a reference to it (Not sure what exactly).
// Here continues the correct program: ******---
cell.label.stringValue = array2label[row]
return cell
}
Also see that in the textField case: cell.label.stringValue = array2label[row], you change the string value of the textfield, not the whole NSTextfield.
So guys remember and repeat my words: "I'm not going to change the view of the cell, just its properties". I just spend 4 days to find that...
Here is the NStableCellView promised:
class MyTableCellView: NSTableCellView {
#IBOutlet weak var myView: NSView!
#IBOutlet weak var label: NSTextField!
}
One Image of the view hierarchy:
I want to input in it a 7 languages that the user pick then click OK or Cancel to set it on a label. Sorry for asking a lot but I couldn't find any good answers. All I found was about datepicker i want to do the same page this guy using (Showing a UIPickerView with UIActionSheet in iOS8 not working) can u help me plz with any info thanks in advance
That is the code I used to solve my problem plus the autocomplete for anyone having the same problem as me.
Here is the solution for datepicker with (done,cancel) buttons
var picker = UIPickerView()
var autocompleteUrls = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// The Auto Compelete Delegates
DatePickerTextOutLet.delegate = self
NationalityOutLet.delegate = self
AutoComTable.delegate = self
AutoComTable.dataSource = self
AutoComTable.scrollEnabled = true
AutoComTable.hidden = true
} // ViewdidLoad
///////////////////////////////////Language picker
func PickerSensei(textField:UITextField){
let picker: UIPickerView
picker = UIPickerView(frame: CGRectMake(0, 200, view.frame.width, 200))
picker.backgroundColor = .whiteColor()
picker.showsSelectionIndicator = true
picker.delegate = self
picker.dataSource = self
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.Default
toolBar.translucent = true
toolBar.tintColor = UIColor(red: 226/255, green: 0/255, blue: 0/255, alpha: 1)
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: "donePicker")
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
// let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "CancelPicker")
toolBar.setItems([ spaceButton, doneButton], animated: false)
toolBar.userInteractionEnabled = true
textField.inputView = picker
textField.inputAccessoryView = toolBar
}
func donePicker() {
PickerSensei(FirstnameoutLet)
if ((FirstnameoutLet.text?.characters.count) < 1 ){
FirstnameoutLet.text = "Arabic"
}
FirstnameoutLet.resignFirstResponder()
}
func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
let attributedString = NSAttributedString(string: PreferedLanguagesResult[row], attributes: [NSForegroundColorAttributeName : UIColor.redColor()])
return attributedString
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int{
return 1
}
// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
return PreferedLanguagesResult.count
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
FirstnameoutLet.text = String(PreferedLanguagesResult[row])
// FirstnameoutLet.endEditing(true)
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return self.PreferedLanguagesResult[row]
}
//////////////////////// end of Langauge picker
//////// Date Picker
#IBAction func DatePickeractionButton(sender: AnyObject) {
}
func textFieldDidBeginEditing(textField: UITextField) {
datePicker.datePickerMode = UIDatePickerMode.Date
DatePickerTextOutLet.inputView = datePicker
// datePicker.addTarget(self, action: "datePickerChanged:", forControlEvents: .ValueChanged)
BirthDate = DatePickerTextOutLet.text!
let toolBar = UIToolbar()
toolBar.barStyle = .Default
toolBar.translucent = true
toolBar.tintColor = UIColor(red: 216/255, green: 0/255, blue: 0/255, alpha: 1)
toolBar.sizeToFit()
datePicker.setValue(UIColor.redColor(), forKeyPath: "textColor")
datePicker.backgroundColor = UIColor.whiteColor()
// Adds the buttons
let doneButton = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: "doneClick")
let spaceButton = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .Plain, target: self, action: "cancelClick")
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.userInteractionEnabled = true
// Adds the toolbar to the view
DatePickerTextOutLet.inputView = datePicker
DatePickerTextOutLet.inputAccessoryView = toolBar
// print(BirthDate)
}
func doneClick() {
print(datePicker.date.age)
if ((datePicker.date.age) >= 9460800 && (datePicker.date.age) <= 42048000 ){
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .ShortStyle
dateFormatter.dateFormat = "dd/MM/yyyy"
DatePickerTextOutLet.text = dateFormatter.stringFromDate(datePicker.date)
DatePickerTextOutLet.resignFirstResponder()
}
else if ((datePicker.date.age) <= 9460800 ){
alertWithTitle("Go Home", message: "You are To young my Child", ViewController: self, toFocus: DatePickerTextOutLet)
} //younger than 18 Years Old
else if ((datePicker.date.age) >= 42048000 ){
alertWithTitle("Sorry Sir", message: "You are to Old for This Shit", ViewController: self, toFocus: DatePickerTextOutLet)
}// older than 80 Years Old
}
func cancelClick() {
DatePickerTextOutLet.resignFirstResponder()
}
///////// Date Picker End
/////////////////// that as for the picker now for the auto complete :-
//////////////////////// The Begining of Auto Compelete
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
AutoComTable!.hidden = false
let text = NationalityOutLet.text ?? ""
let substring = ( text as NSString ).stringByReplacingCharactersInRange(range, withString: string )
searchAutocompleteEntriesWithSubstring(substring)
return true
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
// print(substring)
for curString in NationalityIDResults
{
// print(curString)
let myString: NSString! = curString as! NSString
let substringRange: NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(String(curString))
}
}
AutoComTable.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as UITableViewCell!
// print(cell)
if let tempo1 = cell
{
let index = indexPath.row as Int
cell.textLabel!.text = autocompleteUrls[index]
} else
{
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
// var currentTextField = textField.text
NationalityOutLet.text = selectedCell.textLabel!.text
AutoComTable.hidden = true
}
////////////////////////////////////////// The End of Auto Compelete
I have a problem with my collection view, which gives me a 'blink' a second after the view gets appeared on the screen.
Could it be the viewDidLoad or the viewDidAppear that causes this?
This is my current Swift code:
class HomeViewController: UIViewController
{
// MARK: IBOutlets
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var currentUserProfileImageButton: UIButton!
#IBOutlet weak var currentUserFullNameButton: UIButton!
// MARK: - UICollectionViewDataSource
private var interests = [Interest]()
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if PFUser.currentUser() == nil {
// the user hasn't logged in yet
showLogin()
} else {
// the user logged in, do something else
fetchInterests()
let center = NSNotificationCenter.defaultCenter()
let queue = NSOperationQueue.mainQueue()
center.addObserverForName("NewInterestCreated", object: nil, queue: queue, usingBlock: { (notification) -> Void in
if let newInterest = notification.userInfo?["newInterestObject"] as? Interest {
if !self.interestWasDisplayed(newInterest) {
self.interests.insert(newInterest, atIndex: 0)
self.collectionView.reloadData()
}
}
})
}
}
func interestWasDisplayed(newInterest: Interest) -> Bool {
for interest in interests {
if interest.objectId! == newInterest.objectId! {
return true
}
}
return false
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if UIScreen.mainScreen().bounds.size.height == 480.0 {
let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
flowLayout.itemSize = CGSizeMake(250.0, 300.0)
}
configureUserProfile()
}
func configureUserProfile()
{
// configure image button
currentUserProfileImageButton.contentMode = UIViewContentMode.ScaleAspectFill
currentUserProfileImageButton.layer.cornerRadius = currentUserProfileImageButton.bounds.width / 2
currentUserProfileImageButton.layer.masksToBounds = true
}
private struct Storyboard {
static let CellIdentifier = "Interest Cell"
}
#IBAction func userProfileButtonClicked()
{
PFUser.logOut()
showLogin()
}
// MARK: - Fetch Data From Parse
func fetchInterests()
{
let currentUser = User.currentUser()!
let interestIds = currentUser.interestIds
if interestIds?.count > 0
{
let interestQuery = PFQuery(className: Interest.parseClassName())
interestQuery.orderByDescending("updatedAt")
interestQuery.cachePolicy = PFCachePolicy.NetworkElseCache
interestQuery.whereKey("objectId", containedIn: interestIds)
interestQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
if let interestObjects = objects as [PFObject]? {
self.interests.removeAll()
for interestObject in interestObjects {
let interest = interestObject as! Interest
self.interests.append(interest)
}
self.collectionView.reloadData()
}
} else {
print("\(error!.localizedDescription)")
}
})
}
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Show Interest" {
let cell = sender as! InterestCollectionViewCell
let interest = cell.interest
let navigationViewController = segue.destinationViewController as! UINavigationController
let interestViewController = navigationViewController.topViewController as! InterestViewController
interestViewController.interest = interest
} else if segue.identifier == "CreateNewInterest" {
_ = segue.destinationViewController as! NewInterestViewController
} else if segue.identifier == "Show Discover" {
_ = segue.destinationViewController as! DiscoverViewController
}
}
func showLogin()
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let welcomeNavigationVC = storyboard.instantiateViewControllerWithIdentifier("WelcomeNavigationViewController") as! UINavigationController
self.presentViewController(welcomeNavigationVC, animated: true, completion: nil)
}
}
extension HomeViewController : UICollectionViewDataSource{
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return interests.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(Storyboard.CellIdentifier, forIndexPath: indexPath) as! InterestCollectionViewCell
cell.interest = self.interests[indexPath.item]
return cell
}
}
extension HomeViewController : UIScrollViewDelegate{
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
{
let layout = self.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing
var offset = targetContentOffset.memory
let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing
let roundedIndex = round(index)
offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top)
targetContentOffset.memory = offset
}
}
extension HomeViewController : PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate{
func presentLoginViewController()
{
let logInController = PFLogInViewController()
let signupController = PFSignUpViewController()
signupController.delegate = self
logInController.delegate = self
logInController.fields = [PFLogInFields.UsernameAndPassword, PFLogInFields.LogInButton, PFLogInFields.SignUpButton]
logInController.signUpController = signupController
presentViewController(logInController, animated: true, completion: nil)
}
func logInViewController(logInController: PFLogInViewController, didLogInUser user: PFUser)
{
logInController.dismissViewControllerAnimated(true, completion: nil)
}
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser)
{
signUpController.dismissViewControllerAnimated(true, completion: nil)
}
}
Thanks guys :)
All went fine until my project won't compile.I see those things on two of my files.
Terminator found in the middle of a basic block!
label %50
LLVM ERROR: Broken function found, compilation aborted!
Terminator found in the middle of a basic block!
label %71
LLVM ERROR: Broken function found, compilation aborted!
And the error the compiler give
Command /Applications/Xcode-beta 2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1
I tried solving it, but no success.
Older version of the files compile.
The problem is in the files,but what is the terminator?
Where is the problem?
EDIT
Here is some code, the classes are big
LandscapeViewController
import UIKit
class LandscapeViewController: UIViewController,UIScrollViewDelegate {
// MARK: Properties
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
var search: Search!
private var firstTime = true
private var downloadTasks = [NSURLSessionDownloadTask]()
#IBAction func pageChange(sender: UIPageControl){
UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.scrollView.contentOffset = CGPoint(x: self.scrollView.bounds.size.width * CGFloat(sender.currentPage), y: 0)
}, completion: nil )
}
// MARK: Buttons
private func tileButtons(searchResults: [SearchResult]){
var columnsPerPage = 5
var rowsPerPage = 3
var itemWidth: CGFloat = 96
var itemHeight: CGFloat = 88
var marginX: CGFloat = 0
var marginY: CGFloat = 20
let buttonWidth: CGFloat = 82
let buttonHeight: CGFloat = 82
let scrollViewWidth = scrollView.bounds.size.width
switch scrollViewWidth{
case 568:
columnsPerPage = 6
itemWidth = 94
marginX = 2
case 667:
columnsPerPage = 7
itemWidth = 95
itemHeight = 98
marginX = 1
marginY = 29
case 736:
columnsPerPage = 8
rowsPerPage = 4
itemWidth = 92
default:
break
}
let paddingHorz = (itemWidth - buttonWidth)/2
let paddingVert = (itemHeight - buttonHeight)/2
var row = 0
var column = 0
var x = marginX
for (index,searchResult) in searchResults.enumerate(){
let button = UIButton(type: .Custom)
button.setBackgroundImage(UIImage(named: "LandscapeButton"), forState: .Normal)
downloadImageForSearchResult(searchResult, andPlaceOnButton: button)
button.tag = 2000 + index
button.addTarget(self, action: Selector("buttonPressed:"), forControlEvents: .TouchUpInside)
button.backgroundColor = UIColor.whiteColor()
button.frame = CGRect(x: x + paddingHorz, y: marginY + CGFloat(row)*itemHeight + paddingVert, width: buttonWidth, height: buttonHeight)
scrollView.addSubview(button)
++row
if row == rowsPerPage{
row = 0
++column
x += itemWidth
if column == columnsPerPage{
column = 0
x += marginX * 2
}
}
}
let buttonsPerPage = columnsPerPage * rowsPerPage
let numPages = 1 + (searchResults.count - 1) / buttonsPerPage
pageControl.numberOfPages = numPages
pageControl.currentPage = 0
scrollView.contentSize = CGSize(width: CGFloat(numPages) * scrollViewWidth, height: scrollView.bounds.size.height)
}
private func downloadImageForSearchResult(searchResult: SearchResult,andPlaceOnButton button: UIButton){
if let url = NSURL(string: searchResult.artworkURL60){
let session = NSURLSession.sharedSession()
let downloadTask = session.downloadTaskWithURL(url,completionHandler: { [weak button] url, response, error in
if error == nil && url != nil{
if let data = NSData(contentsOfURL: url!){
if let image = UIImage(data: data){
let resizedImage = image.resizedImageWithBounds(CGSize(width: 60, height: 60))
dispatch_async(dispatch_get_main_queue()){
if let button = button{
button.setImage(resizedImage, forState: .Normal)
}
}
}
}
}
})
downloadTasks.append(downloadTask!)
downloadTask?.resume()
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail"{
switch search.state{
case .Results(let list):
let destinationViewController = segue.destinationViewController as! DetailViewController
let searchResult = list[sender!.tag - 2000]
destinationViewController.searchResult = searchResult
default:
break
}
}
}
func buttonPressed(sender: UIButton){
performSegueWithIdentifier("ShowDetail", sender: sender)
}
// MARK: UIScrollViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
let width = scrollView.bounds.size.width
let currentPage = Int((scrollView.contentOffset.x + width/2) / width)
pageControl.currentPage = currentPage
}
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
pageControl.numberOfPages = 0
scrollView.backgroundColor = UIColor(patternImage: UIImage(named: "LandscapeBackground")!)
view.removeConstraints(view.constraints)
view.translatesAutoresizingMaskIntoConstraints = true
pageControl.removeConstraints(pageControl.constraints)
pageControl.translatesAutoresizingMaskIntoConstraints = true
scrollView.removeConstraints(scrollView.constraints)
scrollView.translatesAutoresizingMaskIntoConstraints = true
// Do any additional setup after loading the view.
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
scrollView.frame = view.bounds
pageControl.frame = CGRect(x: 0, y: view.frame.size.height - pageControl.frame.size.height, width: view.frame.size.width, height: pageControl.frame.size.height)
if firstTime{
firstTime = false
switch search.state{
case .NotSearchedYet:
break
case .Loading:
showSpinner()
case .NoResults:
showNothingFoundLabel()
case.Results(let list):
tileButtons(list)
}
}
}
private func showNothingFoundLabel() {
let label = UILabel(frame: CGRect.zeroRect)
label.text = "Nothing Found"
label.backgroundColor = UIColor.clearColor()
label.textColor = UIColor.whiteColor()
label.sizeToFit()
var rect = label.frame
rect.size.width = ceil(rect.size.width/2) * 2
rect.size.height = ceil(rect.size.height/2) * 2
label.frame = rect
label.center = CGPoint(x: CGRectGetMidX(scrollView.bounds), y: CGRectGetMidY(scrollView.bounds))
view.addSubview(label)
}
private func showSpinner(){
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
spinner.center = CGPoint(x: CGRectGetMidX(scrollView.bounds) + 0.5, y: CGRectGetMidY(scrollView.bounds) + 0.5)
spinner.tag = 1000
view.addSubview(spinner)
spinner.startAnimating()
}
func searchResultsRecived(){
hideSpinner()
switch search.state {
case .NotSearchedYet, .Loading:
break
case .NoResults:
showNothingFoundLabel()
case .Results(let list):
tileButtons(list)
}
}
private func hideSpinner(){
view.viewWithTag(1000)?.removeFromSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit{
print("deinit \(self)")
for task in downloadTasks{
task.cancel()
}
}
/*
// 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.
}
*/
}
SearchViewController
import UIKit
class SearchViewController: UIViewController,UISearchBarDelegate,UITableViewDataSource,UITableViewDelegate {
private struct TableViewCellIdentifier {
static let searchResultCell = "SearchResultCell"
static let nothingFoundCell = "NothingFoundCell"
static let loadingCell = "LoadingCell"
}
// MARK: LandscapeViewController
override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)
switch newCollection.verticalSizeClass{
case .Compact:
showLandscapeViewWithCoordinator(coordinator)
case .Regular,.Unspecified:
hideLandscapeViewWithCoordinator(coordinator)
}
}
private func showLandscapeViewWithCoordinator(coordinator: UIViewControllerTransitionCoordinator){
precondition(landscapeViewController == nil)
landscapeViewController = storyboard?.instantiateViewControllerWithIdentifier("LandscapeViewController") as? LandscapeViewController
if let controller = landscapeViewController{
controller.search = search
controller.view.frame = view.frame
controller.view.alpha = 0
view.addSubview(controller.view)
addChildViewController(controller)
coordinator.animateAlongsideTransition({ _ in
if self.presentedViewController != nil{
self.dismissViewControllerAnimated(true, completion: nil)
}
self.searchBar.resignFirstResponder()
controller.view.alpha = 1
}, completion: { _ in
controller.didMoveToParentViewController(self)
})
}
}
private func hideLandscapeViewWithCoordinator(coordinator: UIViewControllerTransitionCoordinator){
if let controller = landscapeViewController{
controller.willMoveToParentViewController(nil)
coordinator.animateAlongsideTransition({ _ in
controller.view.alpha = 0
}, completion: { _ in
if self.presentedViewController != nil {
self.dismissViewControllerAnimated(true, completion: nil)
}
controller.view.removeFromSuperview()
controller.removeFromParentViewController()
self.landscapeViewController = nil
})
}
}
private func showNetworkError(){
let alert = UIAlertController(title: "Whoops...", message: "There was an error reading from the iTunes Store. Please try again.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
}
// MARK: Detail ViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
switch search.state{
case . Results(let list):
let indexPath = sender as! NSIndexPath
let searchResult = list[indexPath.row]
let detailViewController = segue.destinationViewController as! DetailViewController
detailViewController.searchResult = searchResult
default:
break
}
}
}
// MARK: Properties
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var segmentedControl: UISegmentedControl!
let search = Search()
private var landscapeViewController: LandscapeViewController?
// MARK: Methodes
#IBAction func segmentedControl(sender: UISegmentedControl) {
performSearch()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.contentInset = UIEdgeInsets(top: 108, left: 0, bottom: 0, right: 0)
tableView.rowHeight = 80
searchBar.becomeFirstResponder()
configureNib(nibName: TableViewCellIdentifier.searchResultCell)
configureNib(nibName: TableViewCellIdentifier.nothingFoundCell)
configureNib(nibName: TableViewCellIdentifier.loadingCell)
}
private func configureNib(nibName nibName: String){
let cellNib = UINib(nibName: nibName, bundle: nil)
tableView.registerNib(cellNib, forCellReuseIdentifier: nibName)
}
// MARK: UISearchBarDelegate
func searchBarSearchButtonClicked(searchBar: UISearchBar){
performSearch()
}
private func performSearch() {
if let category = Search.Category(rawValue: segmentedControl.selectedSegmentIndex){
search.performSearchForText(searchBar.text!, category: category){
success in
if !success{
self.showNetworkError()
}
if let controller = self.landscapeViewController{
controller.searchResultsRecived()
}
self.tableView.reloadData()
}
tableView.reloadData()
searchBar.resignFirstResponder()
}
}
func positionForBar(bar: UIBarPositioning) -> UIBarPosition {
return .TopAttached
}
// MARK: TableView
// MARK: - UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch search.state{
case .NotSearchedYet:
return 0
case .Loading,.NoResults:
return 1
case .Results(let list):
return list.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch search.state{
case .NotSearchedYet:
fatalError("Should never get here")
case .Loading:
let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.loadingCell, forIndexPath: indexPath)
let spinner = cell.viewWithTag(100) as! UIActivityIndicatorView
spinner.startAnimating()
return cell
case .NoResults:
return tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.nothingFoundCell, forIndexPath: indexPath)
case .Results(let list):
let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.searchResultCell, forIndexPath: indexPath) as! SearchResultCell
let searchResult = list[indexPath.row]
cell.configureForSearchResult(searchResult)
return cell
}
}
// MARK: UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("ShowDetail", sender: indexPath)
}
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
switch search.state{
case .NotSearchedYet,.NoResults,.Loading:
return nil
case .Results:
return indexPath
}
}
}
This is a bug in the Swift compiler, you'll want to file a radar with Apple to report this. There's nothing you can do about it otherwise.
I had this error because I had a switch of an enum with a case that contain an Array payload (don't ask me why, I don't know).
I think the enum is the search state inside the tableView:cellForRowAtIndexPath: method of SearchViewController and the prepareForSegue: method of LandscapeViewController (the .Result case contain a list).
In my case, changing the payload to a Set instead of an Array worked. (as a workaroud until it's fixed)