How do I set Custom Keyboad as a default keyboard to UITextView? - swift

I've created custom keyboard and it works fine. But I don't know how to set my custom keyboard to specific UITextField.
This is my KeyboardViewController
import UIKit
class KeyboardViewController: UIInputViewController {
#IBOutlet var nextKeyboardButton: UIButton!
#IBOutlet weak var percentView: UIView!
#IBOutlet weak var hideKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
// Perform custom UI setup here
self.nextKeyboardButton = UIButton(type: .System)
self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
self.nextKeyboardButton.sizeToFit()
self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false
self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)
self.view.addSubview(self.nextKeyboardButton)
let nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0)
let nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
let nib = UINib(nibName: "KeyboardView", bundle: nil)
let objects = nib.instantiateWithOwner(self, options: nil)
view = objects[0] as! UIView;
for view in self.view.subviews {
if let btn = view as? UIButton {
btn.layer.cornerRadius = 5.0
btn.translatesAutoresizingMaskIntoConstraints = false
//btn.addTarget(self, action: "keyPressed:", forControlEvents: .TouchUpInside)
}
}
}
and other delegate methods. So how can I call this ?
Actually the problem is I couldn't import custom keyboard classes from app extension. That's the main problem. I did this all build phases stuff. And I can use the keyboard if I change keyboard myself.

According Apple Documentation
After a user chooses a custom keyboard, it becomes the keyboard for every app the user opens.
Therefor it's not like your app specific thing, it's more like different languages keyboards. As far as you can't specify some specific language keyboard for your UITextField or UITextView you can't specify your custom keyboard as well.
Are you sure that you need Custom Keyboard instead of custom view for data input inside your app? About custom data input views you can read here.

Related

Adding views dynamically to UIStackview

I'm trying to add views(or buttons) to UIStackView dynamically.
At first, the UIStackView has no arranged views (vertically), and
after getting from some http response, several views(buttons) are added to UIStackView.
UIStackView is also autolayout to hold a specific area.
I've tried to find dynamic adding example, but failed.
Anyone can show me the examples of adding view onto UIStackView dynamically?
It may help you. Please follow this points:
Add UIScrollView to your UIViewController in storyboard or XIB.
Initiate an NSMutableArray name it arrViews gets server response and adds view in the array.
Initialise UIStackViewpass arrView array in the init method.
After that UIStackView will be added subview of UIScrollView.
Add constraint programmatically to UIStackView. That's it.
if let response = self.serverResponse {
if let body = response.responseBody {
if let view = body.views {
arrViews = createSubViews(view)
}
}
}
let stackView = UIStackView(arrangedSubviews: arrViews)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 16
stackView.distribution = .fill
self.scrollView.addSubview(stackView)
//constraints
let leading = NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: self.scrollView, attribute: .leading, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(leading)
let trailing = NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: self.scrollView, attribute: .trailing, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(trailing)
let top = NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.scrollView, attribute: .top, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(top)
let bottom = NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: self.scrollView, attribute: .bottom, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(bottom)
let equalWidth = NSLayoutConstraint(item: stackView, attribute: .width, relatedBy: .equal, toItem: self.scrollView, attribute: .width, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(equalWidth)
leading.isActive = true
trailing.isActive = true
top.isActive = true
bottom.isActive = true
equalWidth.isActive = true
Hope it will help you. Happy coding :)
I use this code in one of my projects:
let baseFrame = CGRect(origin: .zero, size: CGSize(width: requiredWidth, height: partitionHeight))
for instrument in instruments {
let partitionView = PartitionOnDemand(instrument: instrument, mode: playbackMode, frame: baseFrame, referenceView: partitionsAnimator)
partitionsStackView.addArrangedSubview(partitionView)
let tab = InstrumentInfoTabContainer.instantiate(with: instrument) {
self.focus(on: instrument)
}
tabsStackView.addArrangedSubview(tab)
}
While trying with answers, I happend to find how to work it.
class ViewController: UIViewController {
#IBOutlet weak var stack: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func onBtn_Create(_ sender: Any) {
createButton("new button ...")
}
#IBAction func onBtn_Delete(_ sender: Any) {
if let v = stack.arrangedSubviews.last {
stack.removeArrangedSubview(v)
v.removeFromSuperview()
}
}
func createButton(_ title: String) {
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
button.backgroundColor = UIColor.blue
button.setTitle(title, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
stack.addArrangedSubview(button)
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
}
And, I anchored to UIStackView, Trailing=0, Leading=0, Top=0, Bottom=8 to TextView.Top
The subviews inside it are intact without any constraints.
Thank you.

UIView.animate works fine in viewDidAppear() but fires instantly anywhere else

I have looked and tried every solution I could find online as to why my animations is not properly firing. DISCLOSURE: they work fine when put in viewDidAppear(). This question has been asked countless times but none of the solutions work. The results of the animations appear but not with the delay specified, they are instant. What I would like is to fade in a my requestRideButton in and out using either isHidden with UIView.transition or alpha with UIView.animate.
I would also like to move the button while it is fading. I have tried every combination of self.view.layoutIfNeeded() both in and out the animate closure with the constraint in and out. I have also tried it with self.view.superview?.layoutIfNeeded()
class RideRequestViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var rideRequestBottomButtonConstraint: NSLayoutConstraint!
#IBOutlet weak var rideRequestButtonTopConstraint: NSLayoutConstraint!
// Views
#IBOutlet var mapView: MKMapView!
#IBOutlet var rideDetailsView: UIView!
#IBOutlet var requestRideButton: UIButton!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Layout map view to fill screen
mapView.frame = view.bounds
// Apply corner radius and shadow styling to floating views
let cornerRadius: CGFloat = 5.0
inputContainerView.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)
originButton.layoutCornerRadiusMask(corners: [.topLeft, .topRight], cornerRadius: cornerRadius)
paymentButton.layoutCornerRadiusMask(corners: .bottomLeft, cornerRadius: cornerRadius)
priceButton.layoutCornerRadiusMask(corners: .bottomRight, cornerRadius: cornerRadius)
rideDetailsView.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)
pilotView.layoutCornerRadiusMask(corners: [.topLeft, .bottomLeft], cornerRadius: cornerRadius)
vehicleView.layoutCornerRadiusMask(corners: [.topRight, .bottomRight], cornerRadius: cornerRadius)
requestRideButton.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)
}
override func viewDidLoad() {
ref = Database.database().reference()
}
func animate() {
self.rideRequestButtonTopConstraint.constant = -44
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
}
#IBAction
private func handleRequestRideButtonTapped() {
switch rideRequestState {
case .none:
// Update to requesting state
rideRequestState = .requesting
// Perform payment request
paymentContext.requestPayment()
case .requesting:
// Do nothing
// Show button
break
case .active:
// Complete the ride
completeActiveRide()
}
}
private func reloadRequestRideButton() {
guard originPlacemark != nil && destinationPlacemark != nil && paymentContext.selectedPaymentMethod != nil else {
// Show disabled state
requestRideButton.backgroundColor = .riderGrayColor
requestRideButton.setTitle("CONFIRM DELIVERY", for: .normal)
requestRideButton.setTitleColor(.black, for: .normal)
requestRideButton.setImage(nil, for: .normal)
requestRideButton.isEnabled = false
return
}
animate() // <--- view just disappears instantly instead
switch rideRequestState {
case .none:
// Show enabled state
requestRideButton.backgroundColor = .riderYellowColor
requestRideButton.setTitle("CONFIRM DELIVERY", for: .normal)
requestRideButton.setTitleColor(.black, for: .normal)
requestRideButton.setImage(nil, for: .normal)
requestRideButton.isEnabled = true
case .requesting:
// Show loading state
requestRideButton.backgroundColor = .riderYellowColor
requestRideButton.setTitle("...", for: .normal)
requestRideButton.setTitleColor(.white, for: .normal)
requestRideButton.setImage(nil, for: .normal)
requestRideButton.isEnabled = false
case .active:
// Show completion state
requestRideButton.backgroundColor = .white
requestRideButton.setTitle("Complete Ride", for: .normal)
requestRideButton.setTitleColor(.riderDarkBlueColor, for: .normal)
requestRideButton.setImage(nil, for: .normal)
requestRideButton.isEnabled = true
}
}
Changes to views to be animated should be inside the animate block.
func animate() {
UIView.animate(withDuration: 1) {
self.rideRequestButtonTopConstraint.constant = -44
self.view.layoutIfNeeded()
}
}
Changing the constraint in the line above will just update a value and move the view. It won't animate it
UPDATE
So based on looking at another answer and some additional research, The author of UIKit suggests that we update constraints and call layoutIfNeeded inside the animation block like above.
Here is the playground I used to test it
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let movableView = UIView()
var topConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
view.addSubview(button)
button.setTitle("Animate", for: .normal)
button.addTarget(self, action: #selector(animate), for: .touchUpInside)
view.addSubview(movableView)
movableView.backgroundColor = .red
movableView.translatesAutoresizingMaskIntoConstraints = false
topConstraint = NSLayoutConstraint(item: movableView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 100)
topConstraint?.isActive = true
NSLayoutConstraint(item: movableView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: movableView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true
NSLayoutConstraint(item: movableView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true
}
#objc func animate() {
UIView.animate(withDuration: 4.0, animations: {
self.topConstraint?.constant = -100
self.view.layoutIfNeeded()
})
}
}
let vc = ViewController()
PlaygroundPage.current.liveView = vc.view

UIButton is outside of ContainerView

I'm new to Swift, and I'm trying to learn how to build iOS apps programmatically, with very limited use of the Storyboard. Right now, the goal is to create a menu at the top of the app, which will contain buttons. Based off of my research, it appears that the best way to do this is to nest the UIButtons inside of a ContainerView. However, the issue is that my UIButton is displayed outside of my ContainerView. Perhaps I need to set constraints on the ViewContainer and my UIButton?
Could you guys please point me in the right direction? Your help is very much appreciated.
Storyboard:
App Simulator (iPhone 7 Plus):
HomeController.swift:
class HomeController: UIViewController {
let topMenuContainerView = UIView()
topMenuContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topMenuContainerView)
let topMenuController = storyboard!.instantiateViewController(withIdentifier: "Top_Menu_View_Controller")
addChildViewController(topMenuController)
topMenuController.view.translatesAutoresizingMaskIntoConstraints = false
topMenuContainerView.addSubview(topMenuController.view)
topMenuController.didMove(toParentViewController: self)
}
TopMenuController.swift:
class TopMenuController: UIViewController {
let createAdButton: UIButton = UIButton(type: UIButtonType.roundedRect)
createAdButton.setTitle("Create Ad", for: UIControlState.normal)
createAdButton.titleLabel?.textColor = UIColor.white
createAdButton.frame = view.frame
createAdButton.backgroundColor = UIColor.white
view.addSubview(createAdButton)
}
Yes, I believe adding constraints will solve your problem. It would be something like this:
let horizontalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerX, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerY, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
view.addConstraints([horizontalConstraint, verticalConstraint])
BUT, wouldn't it be better to have just a UIView inside your Home Controller instead of a View Controller just for the menu?

How to create a sized WKWebView in Swift 3 / iOS 10

I am attempting to add a WKWebView to my app that takes up about ~2/3 of the screen (leaving space at the bottom). Because there seems to be no way to add the WKWebView to the storyboard, I added a regular UIView. Then in my viewDidAppear method I try and create a WKWebView with the same bounds. Is this a sound approach?
My code looks like this:
#IBOutlet var conversationView: UIView!
override func viewDidAppear(_ animated: Bool) {
let webView = WKWebView(frame: conversationView.frame)
if let url = URL(string: "https://www.google.com") {
webView.load(URLRequest(url: url))
}
// conversationView = webView // <-- doesn't work :\
conversationView.addSubview(webView) // works!
}
Swaping the views outright doesn't work at all, so instead I add the web view as a subview of the temporary view I added to the storyboard. This at least lets me constrain it's position and size but still it feels very kludgy and like there must be a better way of doing this..
None of the other answers worked for me. The answer by Samip Shah is along the right track but the constraints are not quite correct. The important thing to remember is to set translatesAutoresizingMaskIntoConstraints to false.
So, here's my solution. In the storyboard create a UIView with the constraints and layout you want for the WKWebView. In your view controller add the code:
#IBOutlet var contentView: UIView!
var wkWebView:WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
wkWebView = WKWebView(frame:contentView.frame)
contentView.addSubview(wkWebView)
constrainView(view: wkWebView, toView: contentView)
let request = URLRequest(url: /* your url here */)
wkWebView.load(request)
}
The code for constraining the view. You might add this to a utilities class if you want to use it more than one view controller.
func constrainView(view:UIView, toView contentView:UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
view.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
That's a very good approach; it is perfectly reasonable to have a "content view" whose job is merely to guide the web view into place, as it were. But it would be even better to add the web view plus constraints that size and position it.
If you want to give constraints that size and postion the webview with
respect to its container view, you can do like this.
#IBOutlet weak var wkWebBackgroundView: UIView!
var wkWebView:WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
setUpView()
}
func setUpView(){
self.wkWebView = WKWebView(frame: CGRect.zero)
self.wkWebBackgroundView.addSubview(wkWebView)
let height = NSLayoutConstraint(item: wkWebView, attribute: .height, relatedBy: .equal, toItem: wkWebBackgroundView, attribute: .height, multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: wkWebView, attribute: .width, relatedBy: .equal, toItem: wkWebBackgroundView, attribute: .width, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: wkWebView, attribute: .top, relatedBy: .equal, toItem: wkWebBackgroundView, attribute: .top, multiplier: 1, constant: 0)
let leading = NSLayoutConstraint(item: wkWebView, attribute: .leading, relatedBy: .equal, toItem: wkWebBackgroundView, attribute: .leading, multiplier: 1, constant: 0)
view.addConstraints([leading, top, height, width])
}
You can just size the webview frame as desired without using a different view.
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height * 0.66).integral)
view.addSubview(webView)
However, if you need the view in the storyboard to line up other subview constraints then your method works fine.
Edit: Just saw your added screenshot with comment stating the view is below a label:
let webView = WKWebView(frame: CGRect(x: label.frame.maxX + (whatever distance you want between label bottom and webView top), y: 0,
width: view.bounds.width, height: view.bounds.height * 0.66).integral)
view.addSubview(webView)
You can try this hope it works
Note: Please set Frame according to your requirements.
import UIKit
import WebKit
class WKWebViewVC: UIViewController {
#IBOutlet weak var wkWebviewBGView: UIView!
var wkWebview: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
wkWebview = WKWebView(frame: wkWebviewBGView.bounds, configuration: WKWebViewConfiguration())
wkWebview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.wkWebviewBGView.addSubview(wkWebview)
let url = URL(string: "https://www.google.com")
wkWebview.load(URLRequest(url: url!))
}
}

Programmatically add class to UIbutton in Swift

I'm new to Swift and am confused as how to programmatically add a custom class to a button. I created a custom class and can add it using the storyboard. It works fine, but how would this be done programmatically?
Can I use the CheckBox class in place of UIButton or would I still need to use UIButton and add the class?
The Class
class CheckBox: UIButton {
//images
let checkedImage = UIImage(named: "checked_checkbox") //as UIImage!
let uncheckedImage = UIImage(named: "unchecked_checkbox") //as UIImage!
//bool property
var isChecked:Bool = false{
didSet{
if isChecked == true {
self.setImage(checkedImage, forState: .Normal)
}else {
self.setImage(uncheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked(sender:UIButton) {
if(sender == self){
if isChecked == true {
println("setting to false")
isChecked = false
}else{
println("setting to true")
isChecked = true
}
}
}
}
Adding the class to the view controller
override func viewDidLoad() {
super.viewDidLoad()
let checkBox = CheckBox()
checkBox.backgroundColor = UIColor.blueColor()
self.view.addSubview(checkBox)
checkBox.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addConstraint(NSLayoutConstraint(item: checkBox, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 50))
self.view.addConstraint(NSLayoutConstraint(item: checkBox, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1.0, constant: 50))
}
the checkbox does not appear
Could that be because you have forgotten to give your checkbox any size?
Also, I notice you have these images:
let checkedImage = UIImage(named: "checked_checkbox") //as UIImage!
let uncheckedImage = UIImage(named: "unchecked_checkbox") //as UIImage!
But could it be that you have forgotten to put either of those images in the button? I don't see any code that does that.
If I call checkbox.isCheck = true or false the checkbox appears but does not change if clicked
Could that be because you have forgotten to give your checkbox any target-action for its control event? Previously you had this code:
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
But now there is no awakeFromNib call - this button does not awake from a nib. The storyboard is the nib. This button is coming from code now.
If you fix that, so that that code is called, then isChecked will be set and the image will appear, won't it?
If you decide to do it programmatically(swift 2.2), and not via the storyboard, make sure you add an override int function, a required init? function, and put the following in your override init:
override init(frame: CGRect)
{
super.init(frame: frame)
self.addTarget(self, action: #selector(CheckBox.ButtonClicked(_:)),
forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
self.setImage(uncheckedImage, forState: .Normal)
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
The important bit is the self.setImage. I couldn't get the images to appear when the View loaded up.