Action method not working OSX - swift

I am trying to handle a click event if the user presses on a textfield. Somehow it is not jumping inside the function. Here is my code:
class ViewController: NSViewController {
override func viewDidLoad() {
let textField2 = NSTextField(frame: CGRectMake(30, 30, 37, 17))
textField2.stringValue = "Label"
textField2.bordered = false
textField2.backgroundColor = NSColor.controlColor()
view.addSubview(textField2)
textField2.action = #selector(myAction)
}
func myAction(sender: NSView)
{
print("aktuell: \(sender)")
currentObject = sender
}
}
If I click on the textfield it is not jumping inside the function.
Does anybody know how to link the textfield to the function? Please note that it has to be that function because I am linking other controls to it as well (e.g. buttons).

You can't link a text field like that. What is it you're trying to accomplish? You probably want the NSTextFieldDelegate methods instead. Look at the help on that protocol. If those have what you want, then just set textField2.delegate = self and then implement the protocol.

Implement - (void)textFieldDidBeginEditing:(UITextField *)textField method of UITextFieldDelegate protocol. For example:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
if textField.isEqual(firstTextField){
//some code
}
if textField.isEqual(secondTextField){
//other code
}
}

Related

Is there a way to grab the parent object from a subview?

I'm working on a crossword puzzle app for swift and I am having trouble getting a UIButton from a subview in the UIButton that is a textfield.
The text field takes up the space where the UIButton title should be, but when clicking on the textfield it doesn't click the UIButton.
The UIButton itself as of now, highlights all the UIButtons of the some column and row.
There a few things I've tried such as graving the superclass of the subview
var myButton: CustomButton = textfield.superclass as? CustomButton
and I also tried using
var myObject : CustomButton? {
return view.compactMap({$0 as? CustomButton }).first
}
In the CustomButton.swift
class CustomButton: UIButton {
var number: Int = 0
var letter: Character?
var textField: UITextField!
var cornerLabel: UILabel!
// this init will intialize the button through programatically
override init(frame: CGRect) {
super.init(frame: frame)
textField = UITextField(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
textField.translatesAutoresizingMaskIntoConstraints = false
textField.center = .zero
textField.textAlignment = .center
textField.text = ""
I have a ButtonStore.swift that stores all the CustomButtons in the array so I can manage them and retrieve certain ones.
And the MainController.swift has all the reference CustomButton. I am using a UITextFieldDelegate from the MainController
class MainController : UIViewController, UITextFieldDelegate{
var buttonStore : ButtonStore!
#IBOutlet var A1 : CustomButton!
#IBAction func buttonIsPress(sender: CustomButton){
let button : CustomButton = sender
let identifier : String = sender.accessibilityIdentifier ?? ""
// Clear all backgroundbox back to white unless the background is black
buttonStore.removeHighlights()
// Highlight the button pressed and its column and row
buttonStore.highlightRowColumn(identifier: identifier)
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
{
print("something happened")
// TRIED RECEIVING THE CUSTOMBUTTON HERE
return true
}
The expected result is to have had the CustomButton trigger when textfield is pressed or grabbing the CustomButton of the textfield pressed so I use the CustomButton as reference.
You can get the superview by
if let button = textField.superview as? CustomButton {
// Do what you want to here
}
You can grab any superview or any parent view controller generically in a type safe way by walking up the responder chain (because both UIViewController and UIView inherit from UIResponder and implement its next method):
extension UIResponder {
func firstParent<T: UIResponder>(ofType type: T.Type ) -> T? {
return next as? T ?? next.flatMap { $0.firstParent(ofType: type) }
}
}
Use like:
if let button = textField.firstParent(ofType: CustomButton.self) {
//Do button stuff here
}
This method has the advantage that you can find the next parent of a particular type even if it isn't the immediate parent (ie you may have several views between the textfield and the CustomButton and this still works, while calling superview does not).

How to determine the last activ UITextField in Xcode

I am new to swift and i am playing around with a little converter app. I Have two TextFields and one "convert" button.
I want to overwrite the non acticv Textfield with the converted value from the last activ TextField.
I know it would be easier with more Textfields and more buttons but i would like to solve it this way.
Can anybody give me an advise?
var NumberC = Int(InputCelcius.text!)!
var NumberF = Int(InputFahrenheit.text!)!
if InputCelcius.isFirstResponder {
if (InputCelcius.text?.isEmpty)! {
NumberC=0
print("NumberC is empty")
}
NumberC = NumberC * 9/5 + 32
InputFahrenheit.text = " \(NumberC)"
}
else if InputFahrenheit.isFirstResponder {
if (InputFahrenheit.text?.isEmpty)! {
NumberF=0
print("NumberF is empty")
}
NumberF = (NumberF - 32) * 5/9
InputCelcius.text = " \(NumberF)"
}
I’d suggest using the UITextFieldDelegate function textFieldDidEndEditing. Make sure you subclass your viewController as a UITextFieldDelegate.
class ViewController: UIViewController, UITextFieldDelegate {
Set each textField's delegate to self in viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
InputFahrenheit.delegate = self
InputCelsius.delegate = self
}
With this method you wouldn't even need a UIButton, but you could use the button to force the textField to end editing like this.
#objc func buttonAction (sender: UIButton) {
self.view.endEditing(true)
}
Which will call this delegate method if the user was editing a textField.
func textFieldDidEndEditing (textField: UITextField) {
if textField.isEmpty {return} //this prevents nil errors and user would have to type in "0" if wishing to convert zero.
if textField == InputCelsius {
let conversion = Float(textField.text!) * 9/5 + 32
InputFahrenheit.text = String(format: “%.f”, conversion) //“%.1f” will show one decimal place and so forth
} else if textField == InputFahrenheit {
let conversion = ( Float(textField.text!) - 32 ) * 5/9
InputCelsius.text = String(format: “%.f”, conversion)
}
}
I‘m not quite sure about that but I think if you press the button, the button itself will become first responder, so your code won‘t work.
The proper way to do that, would be to set IBAction functions that get called when you finish editing one of your textfields. Just connect the Textfields inside your storyboard to the code like you did it with your button.
Then you can set up two functions, one for each of the textfields, so you don‘t have to worry about firstresponder.

Set inputview for Searchbar - Swift

class TestViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var mySearchBar: UISearchBar!
var myOwnInputView = UIView(frame: CGRectMake(100,100,50,50))
overide func viewDidLoad() {
mySearchBar.delegate = self
mySearchBar.inputView = myOwnInputView // errors - "Cannot assign to the result of this expression
}
}
I'm trying to assign a custom input view to my search bar, however based on apple documentation (-sorry could not find link anymore), for UISearchBar it seems to be a read-only value. Looking at this post, it appears that UISearchbar has multiple subviews and I need to get to the UITextField part in order to change the inputview. However, I'm not sufficiently familiar with Obj-C and my attempts to convert the code to Swift have not been successful.
This is the simplest way:
let searchTextField = searchBar.valueForKey("_searchField") as! UITextField
searchTextField.inputView = myOwnInputView
Found out that the textfield is a subview of a subview of UISearchbar.
Code to get to the textfield in swift:
var fakeView: UIView = UIView(frame: CGRectMake(100, 100, 50, 50))
#IBOutlet weak var searchBar: UISearchbar!
override func viewDidLoad() {
fakeView.backgroundColor = UIColor.redColor()
var c = 0
for v in (self.searchBar.subviews[0]).subviews {
c++
println("\(c).\(v)") //you should see two views - UISearchBarBackground and UISearcBarTextField
if let tf = v as? UITextField {
//do stuff to tf here.
//in my case, what I want is:
tf.inputView = fakeView
break
}
}
}}
Result of above code is no keyboard will show up when the searchbar text field is touched, just a red rectangle.
Note - credit goes to Matt Neuburg's "Programming iOS8: Dive Deep into Views, ViewControllers, and Frameworks (ISBN: 978-1491908730). Chapter 8 to be precise.

How do I disable drag and drop on NSTextView?

I have a NSWindowController that contains several NSViewControllers. I would like to universally accept drag and drop events with the NSWindowController class and not be intercepted by other views such as NSTextView (contained in a NSViewController)
How can I tell NSTextView to ignore the drag & drop event?
I found out that there were two things needed to skip past NSTextView's interception of the drag and drop event.
In the NSViewController containing your NSTextView:
- (void)awakeFromNib
{
[self noDragInView:self.view];
}
- (void)noDragInView:(NSView *)view
{
for (NSView *subview in view.subviews)
{
[subview unregisterDraggedTypes];
if (subview.subviews.count) [self noDragInView:subview];
}
}
Now subclass your NSTextView and add this method:
- (NSArray *)acceptableDragTypes
{
return nil;
}
The NSTextView should now properly ignore the drag and drop event and leave it to be handled by the NSWindow.
It is sufficient to subclass the NSTextView and override the getter for its acceptableDragTypes property, no need to unregisterDraggedTypes. In Swift:
override var acceptableDragTypes : [String] {
return [String]()
}
Swift 5
import Cocoa
class NSTextViewNoDrop: NSTextView {
override var acceptableDragTypes: [NSPasteboard.PasteboardType] { return [] }
}
Slight update.
import Cocoa
class MyTextView : NSTextView {
// don't accept any drag types into the text view
override var acceptableDragTypes : [NSPasteboard.PasteboardType] {
return [NSPasteboard.PasteboardType]()
}
}

iphone keyboard without textview

is it possible to bring up the keyboard in an iphone app without a textview? or will i have to have an invisible textview?
if so, how do you programatically create a textview and then bring up the keyboard (without the user having to tap the textview)? the only examples i can find use interface builder..
The only (valid) way to show the keyboard is to have a textfield that is first responder.
You can hide it and make it first responder programmatically by calling becomeFirstResponder on the hidden textfield.
You can create a UITextView programmatically by doing something like this (assume aRect and view exist)
var textView = [[[UITextView alloc] initWithFrame:aRect] autorelease];
[view addSubview:textView];
[textView becomeFirstResponder];
UIKeyInput is your friend:
protocol KeyboardInputControlDelegate: class {
func keyboardInputControl( keyboardInputControl:KeyboardInputControl, didPressKey key:Character)
}
class KeyboardInputControl: UIControl, UIKeyInput {
// MARK: - properties
weak var delegate: KeyboardInputControlDelegate?
// MARK: - init
override init(frame: CGRect) {
super.init(frame: frame)
addTarget(self, action: Selector("onTouchUpInside:"), forControlEvents: .TouchUpInside)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - UIView
override func canBecomeFirstResponder() -> Bool {
return true
}
// MARK: - methods
dynamic private func onTouchUpInside(sender: KeyboardInputControl) {
becomeFirstResponder()
}
// MARK: - UIKeyInput
var text:String = ""
func hasText() -> Bool {
return text.isEmpty
}
func insertText(text: String) {
self.text = text
for ch in text {
delegate?.keyboardInputControl(self, didPressKey: ch)
}
}
func deleteBackward() {
if !text.isEmpty {
let newText = text[text.startIndex..<text.endIndex.predecessor()]
text = newText
}
}
}
Example usage. Tap the red view and see the Xcode console output:
class ViewController: UIViewController, KeyboardInputControlDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let kic = KeyboardInputControl(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
kic.delegate = self
kic.backgroundColor = UIColor.redColor()
view.addSubview(kic)
}
func keyboardInputControl(keyboardInputControl: KeyboardInputControl, didPressKey key: Character) {
println("Did press: \(key)")
}
}
After some more digging, I found this. It's unofficial, but I bet it works.
UIKeyboard *keyboard = [[[UIKeyboard alloc] initWithFrame: CGRectMake(0.0f, contentRect.size.height - 216.0f, contentRect.size.width, 216.0f)] autorelease];
[keyboard setReturnKeyEnabled:NO];
[keyboard setTapDelegate:editingTextView];
[inputView addSubview:keyboard];
The way this stuff works is via the NSNotificationCenter publish/subscribe model. First you need to use addObserver:selector:name:object:, then you can try doing this:
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:NSTextDidBeginEditingNotification object:self]];
But I'm not sure what notifications you would get, or would need to register for, to get the keyboard typing character values. Good luck and happy hacking :)