iOS (swift) exit/dismiss preselect textView in share extension - ios

when launching the iOS share extension the textView will by default already be selected / entered. (the keyboard will be visible and the textView will be in edit mode)
I don't want this to happen, how do I programatically exit the textView
override func viewDidLoad() {
self.textView.exit() // obviously doesn't work
}
I see tons of posts about how to exit when user press enter on the keyboard, I do not want to do it "when something delegate" I just want the textview to not be in edit mode when the extension is launched (on viewDidLoad).
I have also tried (as suggested in other post)
self.textView.endEditing(true)
which did not hide the keyboard or exit the textView

You can call textView.resignFirstResponder() in presentationAnimationDidFinish
class ShareViewController: SLComposeServiceViewController {
var textViewTintColor: UIColor?
override func viewDidLoad() {
super.viewDidLoad()
// hide cursor which appears during presentation animation
self.textViewTintColor = self.textView.tintColor
self.textView.tintColor = .clear
}
override func presentationAnimationDidFinish() {
super.presentationAnimationDidFinish()
guard let tintColor = self.textViewTintColor else { return }
self.textView.resignFirstResponder()
// reset cursor
self.textView.tintColor = tintColor
self.textViewTintColor = nil
}
}

Related

inputAccessoryView appearing with animation after view appears in iOS

I have an inputAccessoryView for text input in a chat app. I implemented the inputAccessoryView using the following:
override var inputAccessoryView: UIView? {
get {
setupInputToolbar()
return inputToolbar
}
}
override var canBecomeFirstResponder: Bool {
return true
}
This occurs in a viewController which is a childViewController. I use a segmented control to display this chat viewController with the inputAccessoryView.
The problem I'm having is the inputAccessoryView will only display if I place self.becomeFirstResponder() in the viewDidAppear() of the chat viewController (child).
If I omit self.becomeFirstResponder() or place it in viewDidLoad() or viewWillAppear(), the inputAccessoryView does not display.
The problem with having it in viewDidAppear() is that it displays with an animation after the view is already on screen which is not what I want.
I accomplished this by first adding a viewHasPerformedSubviewLayoutAtLeastOnce property to the ViewController.
private var viewHasPerformedSubviewLayoutAtLeastOnce = false
Making canBecomeFirstResponder dependent on the above property.
override var canBecomeFirstResponder: Bool {
return viewHasPerformedSubviewLayoutAtLeastOnce
}
Then updating that property in viewDidLayoutSubviews() and becoming first responder inside a UIView.performWithoutAnimation block.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if viewHasPerformedSubviewLayoutAtLeastOnce == false {
viewHasPerformedSubviewLayoutAtLeastOnce = true
UIView.performWithoutAnimation { becomeFirstResponder() }
}
}
It all feels a little hacky, but it results in the view appearing without any animations.

Make UILabel focusable and tappable (tvOS)

I'm trying to implement 6 lines high description label and I want it to be focusable. Ideally that would mean extending UILabel class to make a custom component. I tried that by implementing canBecomeFocused and didUpdateFocusInContext but my UILabel doesn't seem to get any focus.
I also tried replacing UILabel with UIButton, but buttons aren't really optimised for this sort of thing. Also that would mean I'd need to change buttonType on focus from custom to plain.. and buttonType seems to be a ready-only property.
In reality I'd like to have exact same text label implemented by Apple in Apple TV Movies app. For movie description they have a text label that displays a few lines of text and a "more". When focused it looks like a button (shadows around) and changed background color. When tapped - it opens up a modal window with entire movie description.
Any suggestions? Or maybe someone has already implemented this custom control for tvOS? Or event better - there is a component from Apple that does this and I'm missing something.
P.S: Swift solution would be welcome :)
Ok, answering my own question :)
So it appears that some some views are "focusable" on tvOS out-of-the-box, and other have to be instructed to do so.
I finally ended up using UITextView, which has a selectable property, but if not one of these focusable views by default. Editing of TextView has to be disabled to make it look like UILabel. Also, currently there is a bug which prevents you from using selectable property from Interface Builder but works from code.
Naturally, canBecomeFocused() and didUpdateFocusInContext had to be implemented too. You'll also need to pass a UIViewController because UITextView is not capable of presenting a modal view controller. Bellow is what I ended up creating.
class FocusableText: UITextView {
var data: String?
var parentView: UIViewController?
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: "tapped:")
tap.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]
self.addGestureRecognizer(tap)
}
func tapped(gesture: UITapGestureRecognizer) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let descriptionView = storyboard.instantiateViewControllerWithIdentifier("descriptionView") as? DescriptionViewController {
if let view = parentView {
if let show = show {
descriptionView.descriptionText = self.data
view.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
view.presentViewController(descriptionView, animated: true, completion: nil)
}
}
}
}
override func canBecomeFocused() -> Bool {
return true
}
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if context.nextFocusedView == self {
coordinator.addCoordinatedAnimations({ () -> Void in
self.layer.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.2).CGColor
}, completion: nil)
} else if context.previouslyFocusedView == self {
coordinator.addCoordinatedAnimations({ () -> Void in
self.layer.backgroundColor = UIColor.clearColor().CGColor
}, completion: nil)
}
}
}
As for making a UILabel focusable:
class MyLabel: UILabel {
override var canBecomeFocused: Bool {
return true
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocus(in: context, with: coordinator)
backgroundColor = context.nextFocusedView == self ? .blue:.red
}
}
IMPORTANT!!!
As stated on the apple developer portal:
The value of this property is true if the view can become focused; false otherwise.
By default, the value of this property is false. This property informs the focus engine if a view is capable of being focused. Sometimes even if a view returns true, a view may not be focusable for the following reasons:
The view is hidden.
The view has alpha set to 0.
The view has userInteractionEnabled set to false.
The view is not currently in the view hierarchy.
Use a collection view with just one cell and add transform to cell and change cell background color in didUpdateFocusInContext when focus moves to cell.
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
coordinator.addCoordinatedAnimations({
if self.focused {
self.transform = CGAffineTransformMakeScale(1.01, 1.01)
self.backgroundColor = UIColor.whiteColor()
self.textLabel.textColor = .blackColor()
}
else {
self.transform = CGAffineTransformMakeScale(1, 1)
self.backgroundColor = UIColor.clearColor()
self.textLabel.textColor = .whiteColor()
}
}, completion: nil)
}
As an additional step you could try to extract the color of the image if you are using the image as background like iTunes and use that for Visual effect view behind the cell.
Also you can apply transform to the collectionView in the video controller to make it look like in focus
You can use system button, and set the background image in storyboard to an image that contains the color you would like

How do I activate an inputView using a UIButton in Swift?

I am attempting to have a UIDatePicker come up as a keyboard when the user hits a UIButton. I was able to get it to work with a textfield, but I don't like how the cursor is visible and the user could enter in any text if they had an external keyboard. Here is my code:
#IBAction func dateFieldStart(sender: UITextField) {
var datePickerStartView : UIDatePicker = UIDatePicker()
datePickerStartView.datePickerMode = UIDatePickerMode.Time
sender.inputView = datePickerStartView // error when sender is UIButton
}
I tried changing the sender to UIButton but it gave this error on the line that is marked above:
Cannot assign to 'inputView' in 'sender'
I have tried researching it and no one else seems to have had a problem with it. Anyone know how to trigger a UIDatePicker inputView using a UIButton or anything that might work better that the user cannot type into? Thanks!
This is years after the original question, but for anyone who may be looking for solution to this you can subclass UIButton and provide a getter and setter for the inputView property. Be sure to call becomeFirstResponder in the setter and override canBecomeFirstResponder. For example:
class MyButton: UIButton {
var myView: UIView? = UIView()
var toolBarView: UIView? = UIView()
override var inputView: UIView? {
get {
myView
}
set {
myView = newValue
becomeFirstResponder()
}
}
override var inputAccessoryView: UIView? {
get {
toolBarView
}
set {
toolBarView = newValue
}
}
override var canBecomeFirstResponder: Bool {
true
}
}
let tempInput = UITextField( frame:CGRect.zero )
tempInput.inputView = self.myPickerView // Your picker
self.view.addSubview( tempInput )
tempInput.becomeFirstResponder()
It's a good idea to keep a reference to tempInput so you can clean-up on close
I wanted to do the same thing, I ended up just overlaying a UITextField over the button and using the inputView of that instead.
Tip: set tintColor of the UITextField to UIColor.clearColor() to hide the cursor.
You can create a view for the picker off screen view and move it on screen when you need it. Here's another post on this.

Disabling user input for UITextfield in swift

pretty trivial question, I know. But I can not find anything online.
I need to disable the user from being able to edit the text inside of a text field. So that when the click on the text, a keyboard doesn't show up.
Any ideas?
A programmatic solution or if it is possible through storyboards would be great.
Try this:
Swift 2.0:
textField.userInteractionEnabled = false
Swift 3.0:
textField.isUserInteractionEnabled = false
Or in storyboard uncheck "User Interaction Enabled"
Another solution, declare your controller as UITextFieldDelegate, implement this call-back:
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if textField == myTextField {
return false; //do not show keyboard nor cursor
}
return true
}
In storyboard you have two choise:
set the control's 'enable' to false.
set the view's 'user interaction enable' false
The diffierence between these choise is:
the appearance of UITextfild to display in the screen.
First is set the control's enable. You can see the backgroud color is
changed.
Second is set the view's 'User interaction enable'. The backgroud color is NOT changed.
Within code:
textfield.enable = false
textfield.userInteractionEnabled = NO
Updated for Swift 3
textField.isEnabled = false
textfield.isUserInteractionEnabled = false
If you want to do it while keeping the user interaction on.
In my case I am using (or rather misusing) isFocused
self.myField.inputView = UIView()
This way it will focus but keyboard won't show up.
I like to do it like old times. You just use a custom UITextField Class like this one:
//
// ReadOnlyTextField.swift
// MediFormulas
//
// Created by Oscar Rodriguez on 6/21/17.
// Copyright © 2017 Nica Code. All rights reserved.
//
import UIKit
class ReadOnlyTextField: UITextField {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override init(frame: CGRect) {
super.init(frame: frame)
// Avoid keyboard to show up
self.inputView = UIView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Avoid keyboard to show up
self.inputView = UIView()
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
// Avoid cut and paste option show up
if (action == #selector(self.cut(_:))) {
return false
} else if (action == #selector(self.paste(_:))) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
In swift 5, I used following code to disable the textfield
override func viewDidLoad() {
super.viewDidLoad()
self.textfield.isEnabled = false
//e.g
self.design.isEnabled = false
}
Swift 4.2 / Xcode 10.1:
Just uncheck behavior Enabled in your storyboard -> attributes inspector.
you can use UILabel instead if you don't want the user to be able to modify anything in your UITextField
A programmatic solution would be to use enabled property:
yourTextField.enabled = false
A way to do it in a storyboard:
Uncheck the Enabled checkbox in the properties of your UITextField
textField.isUserInteractionEnabled = false

error passing data between ViewController

I've a strange issue. I have a UITextField I can write inside and the text is displayed directly in UITextView. I'm using a UITabBarController to switch between the UIViewController.
I'm using that class to save data (wrote in TabBarController.swift):
public class ModelData {
var text = ""
var color:UIColor? = nil
}
That code to save the data (wrote in ViewController.swift):
override func viewDidDisappear(animated: Bool) {
let model = (self.tabBarController as TabBarViewController).model
model.color = textview.textColor
model.text = textview.text
}
And that code to give the data (wrote in SecondViewController.swift):
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let model = (self.tabBarController as TabBarViewController).model
textview.textColor = model.color
textview.text = model.text
}
So, my problem is that: Because I use a UItextField where I can write inside I have disabled User Interaction in UITextView, but now I need to enable that options because I want scroll the UITextView. But now when I pass between the ViewController the data is not saved. (only the color is not saved)
For whatever reason, (it looks like a bug), the textColor property of UITextView returns nil when Selectable is unchecked in Interface Builder. It is not a problem if the selectable property is set to false in code. So a workaround is to leave Selectable checked in Interface Builder and set it to false in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Set selectable to false here so that the user can't select the
// textview text, but User Interaction is still enabled to allow them
// to scroll it. This is a workaround for a problem in Interface
// Builder which causes the textColor property to return nil if Selectable
// is unchecked in Interface Builder.
textview.selectable = false
}

Resources