iphone keyboard without textview - iphone

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 :)

Related

How to hide/show Xib view on button click in swift

I have created xib view(AdvertisementView) programatically and here i have added Delegate method for button
import UIKit
protocol SampleButtonViewDelegate: AnyObject {
func sampleButtonTapped()
}
class AdvertisementView: UIView {
// MARK: - MySubViews
//some other view...
lazy var closeButton: UIButton = {
let buttonClose = UIButton(type: .roundedRect)
buttonClose.translatesAutoresizingMaskIntoConstraints = false
buttonClose.backgroundColor = .red
buttonClose.setTitle("X", for: .normal)
buttonClose.addAction {
print("this is close button")
self.delegate?.sampleButtonTapped()
}
return buttonClose
}()
// MARK: - Properties
var website = ""
weak var delegate: SampleButtonViewDelegate?
// MARK: - Initializers
override init(frame: CGRect) {
super.init(frame: frame)
clipsToBounds = true
addCloseButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
clipsToBounds = true
addCloseButton()
}
private func addCloseButton() {
addSubview(closeButton)
NSLayoutConstraint.activate([
closeButton.topAnchor.constraint(equalTo: titleLabel.topAnchor),
closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 15),
closeButton.widthAnchor.constraint(equalToConstant: 35),
closeButton.heightAnchor.constraint(equalToConstant: 25)
])
}
}
in global.swift file i have added AdvertisementView to containerview--- this is global file
let adView = AdvertisementView()
func addAdvertisementView(containerView: UIView) {
// let adView = AdvertisementView()
containerView.isHidden = false
containerView.clipsToBounds = true
containerView.addSubview(adView)
adView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
adView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 5),
adView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -5),
adView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 5),
adView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -5)
])
}
and in parent class i have added view in storyboard and given its IBOutlet as adContainerView: with this code i can able to close adContainerView when i click close button
after closing if i move from messagelist view controller and come back then again i want to show whole adContainerView with adView but i am getting only adContainerView but on top adView design is not coming why?
how to show adContainerView with adView`... please guide me
class MessageList: UIViewController, SampleButtonViewDelegate {
#IBOutlet weak var adContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
addAdvertisementView(containerView: adContainerView)
adView.delegate = self
}
func sampleButtonTapped() {
adContainerView.isHidden = true
}
}
When you move to messagelist view controller, the previous view was not deinit. It just only hide so all the property you set still remain there. Apple calls it View Controller Life Cycle.
In here is adContainerView still being hidden by the func sampleButtonTapped() just like you describe
So if you need to unhide it when you get back from another view. You show check it in viewWillAppear because the view will appear again not from initialize
Code will be like this - You can add more condition to check if you need to unhide it
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if (adContainerView.isHidden) {
adContainerView.isHidden = false
}
}

NSTouchBar not releasing last touched view

I am making an app which uses the NSTouchBar.
The touchbar is made by the NSWindowController.makeTouchBar()method.
In this touchbar I can place NSCustomTouchBarItems.
I have made two NSCustomTouchBarItems.
The first one sets a view to a default ui button, with this:
let item001 = NSCustomTouchBarItem(identifier: someIdentifier)
item001.view = NSButton(title: "myButton", target: nil, action: nil)
The second one sets a viewController, with this:
let item002 = NSCustomTouchBarItem(identifier: someIdentifier)
item002.viewController = TestViewController()
The TestViewController only loads a simple view inside its loadView()
method.
class TestViewController: NSViewController {
override func loadView() {
self.view = TestView001(frame: NSRect(x: 0, y: 0, width: 100, height: 30))
}
}
The TestView001 only creates a background color so you can see it.
TestView001 has the following code:
class TestView001: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
print("TestView001.init()")
// Create a background color.
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.green.cgColor
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
All of this works perfectly.
But when I have touched the second item inside the touchbar,
and then close my app's window.
The windowController and everything else is nicely released
from memory.
But I can still see that TestView001 is in memory and not being
released.
When using a standard ui button like in item001, then you don't
have this problem.
It looks like some NSTouch still has a reference to the view
if you look at this image:
However, I do not completely understand this image.
What is the best way of solving this.
Thanks in advance.

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 Implement NSSlider Accessible for VoiceOver on MacOS?

I have a slider:NSSlider and valueLabel:NSTextField, and I'm wondering what's the proper way to make it accessible for VoiceOver users.
First I connected a send action for slider to sliderChanged function to update valueLabel.
valueLabel.stringValue = String(slider.integerValue)
VoiceOver reads the label correctly, but it reads the slider in percentage. To fix this, I changed sliderChanged function to setAccessibilityValueDescription.
slider.setAccessibilityValueDescription(String(slider.integerValue))
Now VoiceOver correctly reads the value for the slider. However, it sees both valueLabel and slider, so it's redundant.
I tried valueLabel.setAccessibilityElement(false), but VoiceOver doesn't seem to ignore.
Could someone advise what would be the proper way to implement this? Thanks!
The best way to do this is to create a custom "ContainerView" class (which inherits from UIView) that contains the label and the slider, make the ContainerView an accessibilityElement, and set its accessibilityTraits to "adjustable." By creating a ContainerView that holds both the valueLabel and the slider, you remove the redundancy that is present in your current implementation, while not affecting the layout or usability of the slider/valueLabel for a non-VoiceOver user. This answer is based on this video, so if something is unclear or you want more in-depth info, please watch the video!
Setting a view's UIAccessibilityTraits to be "Adjustable" allows you to use its functions accessibilityIncrement and accessibilityDecrement, so that you can update whatever you need to (slider, textfield, etc). This trait allows any view to act like a typical adjustable (without having to add UIGestureRecognizers or additional VoiceOver announcements).
I posted my code below for convenience, but it is heavily based on the video that I linked to above. (I personally am an iOS developer, so my Swift code is iOS-based)
Note -- I had to override the "accessibilityValue" variable -- this was to make VoiceOver announce changes in the slider whenever the user swiped up or down.
My ContainerView class contains the following code:
class ContainerView: UIView {
static let LABEL_TAG = 1
static let SLIDER_TAG = 2
var valueLabel: UILabel {
return self.viewWithTag(ContainerView.LABEL_TAG) as! UILabel
}
var slider: UISlider {
return self.viewWithTag(ContainerView.SLIDER_TAG) as! UISlider
}
override var accessibilityValue: String? {
get { return valueLabel.text }
set {}
}
override var isAccessibilityElement: Bool {
get { return true }
set { }
}
override var accessibilityTraits: UIAccessibilityTraits {
get { return UIAccessibilityTraitAdjustable }
set { }
}
override init(frame: CGRect) {
super.init(frame: frame)
valueUpdated()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
valueUpdated()
}
func valueUpdated() {
valueLabel.text = String(slider.value)
slider.sendActions(for: .valueChanged)
}
override func accessibilityIncrement() {
super.accessibilityIncrement()
slider.setValue(slider.value + 1, animated: true)
valueUpdated()
}
override func accessibilityDecrement() {
super.accessibilityDecrement()
slider.setValue(slider.value - 1, animated: true)
valueUpdated()
}
}
Hope this helps!

Swift Message Text Area

I am creating a new app and I want to add message area like:
When user click the text area keyboard will shown and user start writing message right button will be send button and text area with will be grow like:
What is the easy way to do it?
I hava a idea to do it but I think it is not the the best way. I am thinking to solve it programmaticly. In viewLoad I will create button and text area programmaticly then when user starts writing I will create them again.
As explained in the official apple documentation you can work with UITextField delegate methods to intercept the differents states.
For example you can use textFieldDidBeginEditing(_:) to handle the moment when the user start to edit. Here , you can handle constraints to enlarge your textField (pay attention, you must working with constraints if you use autolayout, dont make frame modifications):
func textFieldDidBeginEditing(textField: UITextField!) {
//
// hide photo button
// change mic button to send button
self.textFieldTrailingConstraint.constant = 10.0
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
}
After that you can handle the other delegates:
func textFieldShouldEndEditing(textField: UITextField!) -> Bool {
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
return true
}
where you can repeat your animation or do whatever you want.
I'd also recommend to solve this in code. Loading the view you'll have to create all buttons and if the user starts writing you should recreate some of the buttons. But keep in Mind that you have to ensure, that the Textfield is the same instance or that you have to resign the responder to it by code.
In addition to textfield delegates, you need to handle keyboard as well:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
NSNotificationCenter.defaultCenter().removeObserver(self)}
func keyboardWillAppear(notification: NSNotification){
var userInfo:NSDictionary = notification.userInfo!
var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
var contentInsets:UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
self.tableView.contentInset = contentInsets
self.tableView.scrollIndicatorInsets = contentInsets
var messageFrame:CGRect = self.commentField.frame
messageFrame.origin.y -= keyboardSize.height
self.commentField.frame = messageFrame}
func keyboardWillDisappear(notification: NSNotification){
var userInfo:NSDictionary = notification.userInfo!
var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.25)
self.tableView.contentInset = UIEdgeInsetsZero
UIView.commitAnimations()
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero
var messageFrame:CGRect = self.commentField.frame
messageFrame.origin.y += keyboardSize.height
self.commentField.frame = messageFrame}