Related
I want to make a button appear when there are 1 or more characters in a text field.
Currently, I have 3 problems:
I can't use the #objc if I do that then I get the following error "#objc can only be used with members of classes, #objc protocols, and concrete extensions of classes".
So basically I can't use the button.
I want to add constraints to the button, but on some of those I want the Safe Area to be the "toItem" but I don't know which "name" it has for that, I used view.safeAreaInsets but I'm not sure if that's the right way to do it.
I want to check how many characters there are in a text field (so I want the button to appear the moment that the user types a letter).
I would think it has to be done in an if statement but I don't know how to check for the characters in a text field.
This is my code for the first problem:
addButton.addTarget(self, action: #selector(buttonAction(_ :)), for: .touchUpInside)
self.view.addSubview(addButton)
#objc func buttonAction(_ : UIButton) {
}
This is for the second one:
let buttonRightConstraint = NSLayoutConstraint(item: addButton, attribute: .right, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view.safeAreaInsets, attribute: .right, multiplier: 1.0, constant: 16)
let buttonTopConstraint = NSLayoutConstraint(item: addButton, attribute: .top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view.safeAreaInsets , attribute: .top , multiplier: 1.0, constant: 16)
This is for the third one:
if (TextFieldName.text!.count > 0) {
}
This is my total code:
import UIKit
extension AddWorkoutController {
func hideKeyboard() {
let tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
class AddWorkoutController: UIViewController {
#IBOutlet weak var TextFieldName: UITextField!
let addButton = UIButton(type: UIButton.ButtonType.custom) as UIButton
override func viewDidLoad() {
super.viewDidLoad()
configureTextFields()
self.hideKeyboard()
addButton.setBackgroundImage(UIImage(named: "Add-Button"), for: UIControl.State.normal)
addButton.setTitle("", for: UIControl.State.normal)
let buttonRightConstraint = NSLayoutConstraint(item: addButton, attribute: .right, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view.safeAreaInsets, attribute: .right, multiplier: 1.0, constant: 16)
let buttonTopConstraint = NSLayoutConstraint(item: addButton, attribute: .top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view.safeAreaInsets , attribute: .top , multiplier: 1.0, constant: 16)
let buttonHeightConstraint = NSLayoutConstraint(item: addButton, attribute: .height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 32)
let buttonWidthConstraint = NSLayoutConstraint(item: addButton, attribute: .width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 64)
addButton.addTarget(self, action: #selector(buttonAction(_ :)), for: .touchUpInside)
self.view.addSubview(addButton)
self.view.addConstraints([buttonRightConstraint, buttonTopConstraint, buttonHeightConstraint, buttonWidthConstraint])
#objc func buttonAction(_ : UIButton) {
}
if (TextFieldName.text!.count > 0) {
}
}
private func configureTextFields () {
TextFieldName.delegate = self
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
extension UIViewController: UITextFieldDelegate {
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
Maybe it sounds like a stupid question but I'm a beginner.
#objc func buttonAction(_ : UIButton)
is a function and she must be outside of viewDidLoad, at the same level
class AddWorkoutController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#objc func buttonAction(_ : UIButton) {
}
}
For me the best solution is to add your addButton in the storyboard with hidden property checked by default (select your button and then go to right panel > Show the attributes inspector > hidden property)
You must add func textFieldDidEndEditing(_ textField: UITextField) in your AddWorkoutController extension. Inside you can add your if (TextFieldName.text!.count > 0) and then change isHidden property of addButton (addButton.isHidden)
extension AddWorkoutController: UITextFieldDelegate {
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
}
}
PS: Be carefull of your indentation for more lisibility and in general variable are written in camelCase You'll find some best practice here => https://github.com/raywenderlich/swift-style-guide
I am trying to create a button using programmed constraints. I am trying to not use the storyboard. I am having trouble unwrapping the button. How do I do this?
import UIKit
var aa: [NSLayoutConstraint] = []
class ViewController: UIViewController {
var btn: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(btn)
let leadingc2 = btn.widthAnchor.constraint(equalToConstant: 80)
let trailingC2 = btn.heightAnchor.constraint(equalToConstant: 50)
let topc2 = btn.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: -50)
let bottomc2 = btn.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -250)
aa = [leadingc2,trailingC2,topc2,bottomc2]
NSLayoutConstraint.activate(aa)
}
}
You do not need to unwrap it. You need to instantiate it before using it.
override func viewDidLoad() {
super.viewDidLoad()
btn = UITextField() // Create the button like this before using it.
self.view.addSubview(btn)
btn.translatesAutoresizingMaskIntoConstraints = false
let leadingc2 = btn.widthAnchor.constraint(equalToConstant: 80)
let trailingC2 = btn.heightAnchor.constraint(equalToConstant: 50)
let topc2 = btn.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: -50)
let bottomc2 = btn.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -250)
aa = [leadingc2,trailingC2,topc2,bottomc2]
NSLayoutConstraint.activate(aa)
}
Any variable declared with ! will be force unwrapped, meaning that if you forget to create an instance and use the variable, it will throw an error and crash your app.
Use this code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
setupConstraints()
}
lazy var button: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Button", for: .normal)
button.backgroundColor = .blue
//your action here
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
private func setupConstraints() {
button.translatesAutoresizingMaskIntoConstraints = false
let top = NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 100)
let left = NSLayoutConstraint(item: button, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 50)
let right = NSLayoutConstraint(item: button, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: -50)
let height = NSLayoutConstraint(item: button, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50)
view.addConstraints([top, left, right, height])
}
#objc func buttonAction() {
print("Button has pressed")
}
}
I have an iOS project in which I am setting up two sliders and corresponding labels.
Labels are for the slider value. When they slide, the value in the labels change.
I have another class in which I set the slider and label after they create and save that instance in the array. The difficulty I face is when I slide the slider, I do not know which slider is slides.
Here is code:
SliderWidget Class
class SliderWidget {
private var label: UILabel?
private var slider: UISlider?
public func setLable(label: UILabel) {
self.label = label
}
public func setSlider(slider: UISlider) {
self.slider = slider
}
public func getLabel() -> UILabel {
return self.label!
}
public func getSlider() -> UISlider {
return slider!
}
}
Here is the controller on which I set slider and label
class WidgetsVC: UIViewController {
private let step: Float = 10
private var labels: UILabel?
private var sliderArray: Array<SliderWidget> = Array()
override func viewDidLoad() {
super.viewDidLoad()
displaySlider(sliderY: 100,labelY: 70)
displaySlider(sliderY: 200, labelY: 170)
}
private func displaySlider(sliderY: Int, labelY: Int) {
let slider = UISlider()
slider.tintColor = #colorLiteral(red: 0.2980392277, green: 0.6862744689, blue: 0.3137254715, alpha: 1)
let label = displayLabel(labelY: labelY, slider: slider)
view.addSubview(slider)
let horizonalContraints =
NSLayoutConstraint(item: slider, attribute: .leadingMargin,
relatedBy: .equal, toItem: view,
attribute: .leadingMargin, multiplier: 1.0,
constant: 20)
let horizonal2Contraints =
NSLayoutConstraint(item: slider, attribute: .trailingMargin,
relatedBy: .equal, toItem: view,
attribute: .trailingMargin, multiplier: 1.0,
constant: -20)
let pinTop =
NSLayoutConstraint(item: slider, attribute: .top,
relatedBy: .equal, toItem: view,
attribute: .top, multiplier: 1.0,
constant: CGFloat(sliderY))
slider.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([horizonalContraints, horizonal2Contraints, pinTop])
slider.addTarget(self, action: #selector(WidgetsVC.sliderValueDidChange(_:)), for: .valueChanged)
}
private func displayLabel(labelY: Int, slider: UISlider) -> UILabel {
let label = UILabel()
label.text = "0"
labels = label
view.addSubview(label)
let temp = SliderWidget()
temp.setLable(label: label)
temp.setSlider(slider: slider)
sliderArray.append(temp)
let horizonalContraints =
NSLayoutConstraint(item: label, attribute: .leadingMargin,
relatedBy: .equal, toItem: view,
attribute: .leadingMargin, multiplier: 1.0,
constant: 20)
let pinTop =
NSLayoutConstraint(item: label, attribute: .top,
relatedBy: .equal, toItem: view,
attribute: .top, multiplier: 1.0,
constant: CGFloat(labelY))
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([horizonalContraints, pinTop])
return label
}
}
I am not sure what do you want for exactly but you could do it in this way
#objc func sliderValueDidChange(slider: UISlider) {
print(slider.tag)
let changedSliderWidget = sliderArray.filter { (sliderWidget) -> Bool in
return sliderWidget.getSlider() == slider
}.last
}
changedSliderWidget will contain the widget that you need
The easy way to identify each one is to set tag values:
displaySlider(sliderY: 100,labelY: 70, tag: 1)
displaySlider(sliderY: 200, labelY: 170, tag: 2)
And
private func displayLabel(labelY:Int,slider:UISlider, tag: Int)->UILabel{
let label=UILabel()
label.tag = tag
label.text="0"
....
So you can identify the sliders with their .tag value.
I'm writing an child class but couldn't add gesture recognize for it.
Selector error. Here is my code:
import UIKit
class Alert: KeyboardViewController {
var AlertContainer = UIView()
func ShowAlert(message: String,view: UIView)
{
var tap = UITapGestureRecognizer(target: self, action:Selector("CloseAlertView"))
tap.numberOfTapsRequired = 1
AlertContainer.addGestureRecognizer(tap)
AlertContainer.setTranslatesAutoresizingMaskIntoConstraints(false)
AlertContainer.userInteractionEnabled = true
AlertContainer.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
view.addSubview(AlertContainer)
var CTop = NSLayoutConstraint(item: AlertContainer, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: 0.0)
var CRight = NSLayoutConstraint(item: AlertContainer, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: 0.0)
var CLeft = NSLayoutConstraint(item: AlertContainer, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1.0, constant: 0.0)
var CBottom = NSLayoutConstraint(item: AlertContainer, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
view.addConstraints([CTop,CRight,CLeft,CBottom])
}
func CloseAlertView() {
AlertContainer.removeFromSuperview()
}
}
But when i tap on to this view, error: exc_bad_access or some time unrecognized selector sent to instance. Really don't know why ?
and one more question that i can't access to parent class view by super.view ?
Are you are calling this class in the following way (if not ignore my answer)?
// wrong implementation
func myFunc() {
var a = Alert()
a.ShowAlert("Message", view: view)
}
The previous code is wrong (and causes BAD_ACCESS) because a does not exists anymore after the function finished. So when the gesture is fired, it acts on nothing and crashes.
Solution:
var a = Alert() // a property of the caller class
func myFunc() {
a.ShowAlert("Message", view: view)
}
In this way the life of a is long enough to wait for the gesture.
I've built keyboard this way. (code below)
But, there are some problems like:
when you click on some button, it's click animation taking long to get back.
there is no way to place some common keys as globe symbol for language changing or caps lock
What I want to do is, to modify original iOS keyboard and add some other buttons.
Is it possible? Any suggestions?
import UIKit
class KeyboardViewController: UIInputViewController {
#IBOutlet var nextKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
let buttonTitles1 = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]
let buttonTitles2 = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
let buttonTitles3 = ["CP", "Z", "X", "C", "V", "B", "N", "M", "BP"]
let buttonTitles4 = ["CHG", "SPACE", "RETURN"]
var row1 = createRowOfButtons(buttonTitles1)
var row2 = createRowOfButtons(buttonTitles2)
var row3 = createRowOfButtons(buttonTitles3)
var row4 = createRowOfButtons(buttonTitles4)
self.view.addSubview(row1)
self.view.addSubview(row2)
self.view.addSubview(row3)
self.view.addSubview(row4)
row1.setTranslatesAutoresizingMaskIntoConstraints(false)
row2.setTranslatesAutoresizingMaskIntoConstraints(false)
row3.setTranslatesAutoresizingMaskIntoConstraints(false)
row4.setTranslatesAutoresizingMaskIntoConstraints(false)
addConstraintsToInputView(self.view, rowViews: [row1, row2, row3, row4])
}
func createRowOfButtons(buttonTitles: [NSString]) -> UIView {
var buttons = [UIButton]()
var keyboardRowView = UIView(frame: CGRectMake(0, 0, 320, 50))
for buttonTitle in buttonTitles{
let button = createButtonWithTitle(buttonTitle)
buttons.append(button)
keyboardRowView.addSubview(button)
}
addIndividualButtonConstraints(buttons, mainView: keyboardRowView)
return keyboardRowView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(textInput: UITextInput) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
var proxy = self.textDocumentProxy as UITextDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
}
func createButtonWithTitle(title: String) -> UIButton {
let button = UIButton.buttonWithType(.System) as UIButton
button.frame = CGRectMake(0, 0, 20, 20)
button.setTitle(title, forState: .Normal)
button.sizeToFit()
button.titleLabel?.font = UIFont.systemFontOfSize(15)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
button.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
return button
}
func didTapButton(sender: AnyObject?) {
let button = sender as UIButton
var proxy = textDocumentProxy as UITextDocumentProxy
if let title = button.titleForState(.Normal) {
switch title {
case "BP" :
proxy.deleteBackward()
case "RETURN" :
proxy.insertText("\n")
case "SPACE" :
proxy.insertText(" ")
case "CHG" :
self.advanceToNextInputMode()
default :
proxy.insertText(title)
}
}
}
func addIndividualButtonConstraints(buttons: [UIButton], mainView: UIView){
for (index, button) in enumerate(buttons) {
var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: mainView, attribute: .Top, multiplier: 1.0, constant: 1)
var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: mainView, attribute: .Bottom, multiplier: 1.0, constant: -1)
var rightConstraint : NSLayoutConstraint!
if index == buttons.count - 1 {
rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: mainView, attribute: .Right, multiplier: 1.0, constant: -1)
}else{
let nextButton = buttons[index+1]
rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: nextButton, attribute: .Left, multiplier: 1.0, constant: -1)
}
var leftConstraint : NSLayoutConstraint!
if index == 0 {
leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: mainView, attribute: .Left, multiplier: 1.0, constant: 1)
}else{
let prevtButton = buttons[index-1]
leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: prevtButton, attribute: .Right, multiplier: 1.0, constant: 1)
let firstButton = buttons[0]
var widthConstraint = NSLayoutConstraint(item: firstButton, attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
widthConstraint.priority = 800
mainView.addConstraint(widthConstraint)
}
mainView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint])
}
}
func addConstraintsToInputView(inputView: UIView, rowViews: [UIView]){
for (index, rowView) in enumerate(rowViews) {
var rightSideConstraint = NSLayoutConstraint(item: rowView, attribute: .Right, relatedBy: .Equal, toItem: inputView, attribute: .Right, multiplier: 1.0, constant: -1)
var leftConstraint = NSLayoutConstraint(item: rowView, attribute: .Left, relatedBy: .Equal, toItem: inputView, attribute: .Left, multiplier: 1.0, constant: 1)
inputView.addConstraints([leftConstraint, rightSideConstraint])
var topConstraint: NSLayoutConstraint
if index == 0 {
topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: inputView, attribute: .Top, multiplier: 1.0, constant: 0)
}else{
let prevRow = rowViews[index-1]
topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: prevRow, attribute: .Bottom, multiplier: 1.0, constant: 0)
let firstRow = rowViews[0]
var heightConstraint = NSLayoutConstraint(item: firstRow, attribute: .Height, relatedBy: .Equal, toItem: rowView, attribute: .Height, multiplier: 1.0, constant: 0)
heightConstraint.priority = 800
inputView.addConstraint(heightConstraint)
}
inputView.addConstraint(topConstraint)
var bottomConstraint: NSLayoutConstraint
if index == rowViews.count - 1 {
bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: inputView, attribute: .Bottom, multiplier: 1.0, constant: 0)
}else{
let nextRow = rowViews[index+1]
bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: nextRow, attribute: .Top, multiplier: 1.0, constant: 0)
}
inputView.addConstraint(bottomConstraint)
}
}
}
Use the github repo below. It's nearly 1:1 immitation of iOS 8 original keyboard and works like a charm
https://github.com/archagon/tasty-imitation-keyboard
when you click on some button, it's click animation taking long to get back.
Your keys are nothing more than UIButtons. The fade-out animation you're seeing is the normal animation for a UIButton—you can see it for yourself on most buttons across the system. Instead of initializing it with UIButtonType.System, use .Custom and set your own appearance for the desired states.
As an example (there are many possibilities):
let button = UIButton(.Custom)
button.setTitleColor(UIColor.redColor(), forState: .Highlighted)
Note that you don't even have to restrict yourself to using UIButton—custom keyboards are a blank slate.
there is no way to place some common keys as globe symbol for language changing or caps lock
From the Custom Keyboard Guide:
The system picks the appropriate “next” keyboard; there is no API to
obtain a list of enabled keyboards or for picking a particular
keyboard to switch to.
So these "special" keys are also up to you to provide, most likely with your own custom icons. If you're using UIButton, that probably means calling setImage:forState:. It looks like most third-party keyboards use a globe icon almost identicial to the system one for the "next" key.
There is no API to mod the system keyboard—you must build one yourself from the ground-up.
For slow animation, you can study these github repo on how they do their animation:
https://github.com/YuAo/WUEmoticonsKeyboard (See the popview when the button gets pressed.)
https://github.com/ayushgoel/AGEmojiKeyboard
https://github.com/kulpreetchilana/Custom-iOS-Keyboards
or search github:https://github.com/search?utf8=✓&q=iOS+keyboard
Note: They are mostly objective-c repo.
Lastly, it might be cheaper to install a custom keyboard than to make one:
http://www.imore.com/best-custom-keyboards-ios-8