VC with UISlider to change textfield on VC2 - ios

I have a View controller that has a tableView embedded in a ContainerView and just below the ContainerView I have a UISlider. I have code on a view controller for the UISlider and code on another view controller that controls the table view.
Properties of the UISlider are set based on the selected text field - this section of code works. I am struggling to create a function/feature that will change the textField value when the UISlider is move. I think the UISlider Action needs to on the code that controls the UISlider, but I cannot determine how to cast the value of the UISlider.setvalue between the two viewController as the slider is moved to update the textField located in a tableCell. Hopefully makes some sense.
// UISlider ViewController
override func viewDidLoad() {
super.viewDidLoad()
sliderOutlet.isContinuous = true
sliderOutlet.tintColor = UIColor.green
self.refreshSlider()
}
#objc func refreshSlider() {
sliderOutlet.minimumValue = Float(GlobalSliderValues.minimumValue)
sliderOutlet.maximumValue = Float(GlobalSliderValues.maximumValue)
sliderOutlet.value = Float(GlobalSliderValues.sliderValue)
// if let chgIntValue = Int(GlobalSliderValues.changeValue)
// { sliderOutlet.setValue(Float(Double(chgIntValue)), animated: true)
// }
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshSlider), name: Notification.Name("refreshSlider"), object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: Notification.Name("refreshSlider"), object: nil)
}
TableView Controller
override func viewDidLoad() {
super.viewDidLoad()
mortgageAmount.addTarget(self, action: #selector(chgTextFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
}
#objc func chgTextFieldDidChange(textField: UITextField)
{
if let chgStringValue = mortgageAmount.text
{
if Double(chgStringValue) ?? 1 > 10000 {
let alert = UIAlertController(title: "Input Error", message: "Value cannot be greater than 10000", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
GlobalSliderValues.minimumValue = 10
GlobalSliderValues.maximumValue = 10000
GlobalSliderValues.sliderValue = Int(mortgageAmount.text!)!
GlobalSliderValues.mortageAmountValue = Float(Int(mortgageAmount.text!)!)
NotificationCenter.default.post(name:Notification.Name("refreshSlider"), object: nil)
if let chgIntValue = Int(chgStringValue)
{ GlobalSliderValues.changeValue.setValue(Float(Double(chgIntValue)), animated: true)
}
}
}
#IBAction func valueChanged(_ sender: UISlider) {
mortgageAmount.text = String(format: "%.2f",Double(sender.value))
}
struct GlobalSliderValues {
static var minimumValue = Int()
static var maximumValue = Int()
static var lowerValue = Int()
static var UpperValue = Int()
static var locationValue = Int()
static var sliderValue = Int()
static var sliderChgValue = ""
}

We don't need notification center to deal with refreshing the slider. Since we are using constants to store value your code can be changed as follows
class ViewController: UIViewController {
#IBOutlet weak var sliderOutlet: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
sliderOutlet.isContinuous = true
sliderOutlet.tintColor = UIColor.green
}
#objc func refreshSlider() {
DispatchQueue.main.async {
self.sliderOutlet.minimumValue = Float(GlobalSliderValues.minimumValue)
self.sliderOutlet.maximumValue = Float(GlobalSliderValues.maximumValue)
self.sliderOutlet.value = Float(GlobalSliderValues.sliderValue)
print(self.sliderOutlet.minimumValue)
print(self.sliderOutlet.maximumValue)
print(self.sliderOutlet.value)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.refreshSlider()
}
}
I entered mortgage amount as so 1098 so slider look like below

Related

Swift Class has another class from Pod with UiViewController and get Nil on outlets because of that

First of all, sorry the english.
I have a project in Swift 4.2. One of the Pods is imported and added to a class "Principal View Controller". This Pod has de UIViewController and i cannot add it on the class because it's used on the Pod and Xcode doesn't alow multiple inheritance.
If I remove the Pod from the class the Iboutlets works without problem but the pod is not working ( it's used for side menu ). If i leave the Pod added, the Iboutlets just get nil value.
I track in a debug the code and pass on 2 of the ViewDidLoad and nothing load value on Outlets if Pod is added.
Maybe someone knows the way to fix this?
**I remove some code from classes because it's way more than the 300000 characters that stackoverflow allow...
//
// PrincipalViewController.swift
//
import UIKit
import SwiftyJSON
import JLSlideMenuController
import Alamofire
class PrincipalViewController: JLSlideNavigationController, UIImagePickerControllerDelegate, URLSessionDownloadDelegate, UIGestureRecognizerDelegate {
#IBOutlet weak var scrollViewWeather: UIScrollView!
#IBOutlet var profileImage:UIImageView!
#IBOutlet var parentView: UIView!
#IBOutlet var lblUserName:UILabel!
#IBOutlet var menuButton:UIButton!
var jsonVar:JSON!
var rutasDisponibles : [Ruta]!
var userDefaults = UserDefaults.standard
var viewDayArray = [UIView](repeating: UIView(), count: 7)
var labelDayArray = [UILabel](repeating: UILabel(), count: 7)
var labelTempDayArray = [UILabel](repeating: UILabel(), count: 7)
var ivDayArray = [UIImageView](repeating: UIImageView(), count: 7)
var taskDescargaKmls : URLSessionTask!
var contador = 1
#IBOutlet weak var btMisRutas: UIButton!
#IBOutlet weak var btRutas: UIButton!
#IBOutlet weak var btInfoMunicipio: UIButton!
var jlSlide: JLSlideNavigationController!
lazy var session : Foundation.URLSession = {
let config = URLSessionConfiguration.ephemeral
config.allowsCellularAccess = false
let session = Foundation.URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
return session
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
UIApplication.shared.isStatusBarHidden = false
profileImage.layer.borderColor = UIColor.lightGray.cgColor
let lang = UserDefaults.standard.string(forKey: "LANG");
btRutas.setTitle(Languages.translateText("rutas", language: lang!), for: UIControl.State())
btInfoMunicipio.setTitle(Languages.translateText("info_municipio", language: lang!), for: UIControl.State())
btMisRutas.setTitle(Languages.translateText("mis_rutas", language: lang!), for: UIControl.State())
if(!OnlineStatus.isConnectedToNetwork()){
scrollViewWeather.isHidden = true
}
}
#IBAction func sideMenu(_ sender: UIButton) {
if menuIsPresented(){
self.hideMenu(animated: true)
}
else{
self.showMenu(animated: true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let configuration = URLSessionConfiguration.background(withIdentifier: "com.website.background")
let gesture = UITapGestureRecognizer(target: self, action: #selector(PrincipalViewController.closeMenu(_:)))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
gesture.delegate = self
self.parentView.addGestureRecognizer(gesture)
addSlideMenu("MenuViewController",storyboardName: "Main",distToTop: 0, widthAspectRatio: 0.8, distToBottom: 0,comeFromLeft: true)
}
The pod is JSlideMenuController : https://cocoapods.org/pods/JLSlideMenuController
//
// JLSlideMenuViewController.swift
// Pods
//
// Created by José Lucas Souza das Chagas on 20/03/16.
//
//
import UIKit
public class JLSlideMenuViewController: UIViewController {
public var attachedNavController:JLSlideNavigationController!
//constraints
var sideDist:NSLayoutConstraint!
var distToTopC:NSLayoutConstraint!
var distToBottomC:NSLayoutConstraint!
var widthC:NSLayoutConstraint!
//
/**
TRUE to enable the menu FALSE to disable
The value of this enable or disable the animations and gestures to show and hide the menu
*/
public var enabled:Bool = true{
didSet{
if enabled == false{
attachedNavController.hideMenu(animated: false)
}
}
}
override public func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.view.layoutIfNeeded()
}
override public func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
//print(parent!.childViewControllers)
}
//public override func canBecomeFirstResponder() -> Bool {
public func canBecomeFirstResponder() -> Bool {
return true
}
override public func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//override public func prefersStatusBarHidden() -> Bool {
public func prefersStatusBarHidden() -> Bool {
return true
}
func removeConstraints(){
self.view.window!.removeConstraint(sideDist)
self.view.window!.removeConstraint(distToTopC)
self.view.window!.removeConstraint(distToBottomC)
self.view.removeConstraint(widthC)
}
/**
Call this method to present modally the correct view controller accordingly to some interaction
with the menu view controller
- parameter VCId: The Id of the view controller to show
- parameter storyboardName: the name of the storyboard where the view controller is
- parameter animated: true to transition with some animation or not
*/
public func presentControllerModally(VCId:String,storyboardName:String,animated:Bool){
let destinyVC = JLSlideNavigationController.loadMenuVC(identifier: VCId, storyboardName: storyboardName)
destinyVC.view.frame = self.attachedNavController.view.frame
self.attachedNavController.hideMenu(animated: false)
let menuSegue = UIStoryboardSegue(identifier: "actualViewToAnother", source: self.attachedNavController,
destination: destinyVC, performHandler: { () -> Void in
DispatchQueue.global(qos: .background).async {
// Background Thread
DispatchQueue.main.async {
// Run UI Updates
self.attachedNavController.topViewController!.present(destinyVC, animated: animated, completion: nil)
}
}
/*
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.attachedNavController.topViewController!.presentViewController(destinyVC, animated: animated, completion: nil)
})
*/
})
//self.prepareForSegue(menuSegue, sender: nil)
self.attachedNavController.topViewController!.prepare(for: menuSegue, sender: nil)
menuSegue.perform()
}
/**
Call this method to show the correct view controller accordingly to some interaction
with the menu view controller
Use this one if the view controller where the menu is right now have some navigation controller
- parameter VCId: The Id of the view controller to show
- parameter storyboardName: the name of the storyboard where the view controller is
- parameter animated: true to transition with some animation or not
*/
public func showController(VCId:String,storyboardName:String,animated:Bool){
let destinyVC = JLSlideNavigationController.loadMenuVC(identifier: VCId, storyboardName: storyboardName)
destinyVC.view.frame = self.attachedNavController.view.frame
self.attachedNavController.hideMenu(animated: false)
let menuSegue = UIStoryboardSegue(identifier: "actualViewToAnother", source: self.attachedNavController,
destination: destinyVC, performHandler: { () -> Void in
self.attachedNavController.pushViewController(destinyVC, animated: animated)
})
//self.prepareForSegue(menuSegue, sender: nil)
self.attachedNavController.prepare(for: menuSegue, sender: nil)
menuSegue.perform()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
//
// JLSlideNavigationController.swift
// Pods
//
// Created by José Lucas Souza das Chagas on 24/04/16.
//
//
import UIKit
open class JLSlideNavigationController: UINavigationController,UINavigationControllerDelegate {
/**
menuVCStoryboardID: The Id of your menu View Controller.
*/
#IBInspectable private(set) var menuVCStoryboardID:String?
/**
the name of the storyboard where your menu View Controller is.
*/
#IBInspectable private(set) var storyboardName:String?
/**
the value of the constraint that indicates the distance between your menu top and this VC top.
*/
#IBInspectable private(set) var distToTop:CGFloat = 0
/**
the value of the constraint that determine your menu Width.
*/
#IBInspectable private(set) var width:CGFloat = 250
/**
the value of the constraint that indicates the distance between your menu bottom and this VC bottom.
*/
#IBInspectable private(set) var distToBottom:CGFloat = 0
/**
the bolean value that indicates if your menu will come from left or from right of the window.
*/
#IBInspectable private(set) var comeFromLeft:Bool = true
/**
A bolean value that indicates to use or not shadow effects
*/
#IBInspectable private(set) var useShadowEffects:Bool = false
private var menuContainerView: UIView?
/**
The instace of your menu View Controller associated to this View Controller
*/
private(set) static var myMenuVC:JLSlideMenuViewController?
public static var panGes:UIPanGestureRecognizer?
public static var screeEdgePanGes:UIScreenEdgePanGestureRecognizer?
/**
Incicates that the pan gesture that were started enabled or not
*/
private var panEnabled:Bool = false
private var lastPanTouch:CGPoint?
override open func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
if let pan = JLSlideNavigationController.panGes{
pan.isEnabled = true
}
if let pan = JLSlideNavigationController.screeEdgePanGes{
pan.isEnabled = true
}
// Do any additional setup after loading the view.
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let _ = self.view.window{
checkIfShouldAddOrUpdateMenu()
}
print(self.view.window!.gestureRecognizers!.count)
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
if let pan = JLSlideNavigationController.panGes{
pan.isEnabled = false
}
if let pan = JLSlideNavigationController.screeEdgePanGes{
pan.isEnabled = false
}
}
class func loadMenuVC(identifier:String,storyboardName:String)->UIViewController{
let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle.main)
return storyboard.instantiateViewController(withIdentifier: identifier)
}
//MARK: - NavigationController delegate methods
public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if let menuView = menuContainerView , let window = self.view.window{
window.bringSubviewToFront(menuView)
}
let subViews = viewController.view.subviews
if let screenEdgeGes = JLSlideNavigationController.screeEdgePanGes{
for subView in subViews{
if let gestures = subView.gestureRecognizers{
for gesture in gestures{
gesture.require(toFail: screenEdgeGes)
}
}
}
}
}
//MARK: - Gestures methods
private func addGestures(){
//Pan
if let window = self.view.window , let pan = JLSlideNavigationController.panGes{
window.removeGestureRecognizer(pan)
}
JLSlideNavigationController.panGes = UIPanGestureRecognizer(target: self, action: #selector(JLSlideNavigationController.panAction(panGes:)))
self.view.window?.addGestureRecognizer(JLSlideNavigationController.panGes!)
//Screen Edge pan
if let window = self.view.window , let pan = JLSlideNavigationController.screeEdgePanGes{
window.removeGestureRecognizer(pan)
}
JLSlideNavigationController.screeEdgePanGes = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(JLSlideNavigationController.screenEdgePanAction(edgePan:)))
if comeFromLeft{
JLSlideNavigationController.screeEdgePanGes!.edges = UIRectEdge.left
}
else{
JLSlideNavigationController.screeEdgePanGes!.edges = UIRectEdge.right
}
self.view.window?.addGestureRecognizer(JLSlideNavigationController.screeEdgePanGes!)
JLSlideNavigationController.panGes!.require(toFail: JLSlideNavigationController.screeEdgePanGes!)
if let screenEdgeGes = JLSlideNavigationController.screeEdgePanGes, let topViewC = topViewController{
let subViews = topViewC.view.subviews
for subView in subViews{
if let gestures = subView.gestureRecognizers{
for gesture in gestures{
gesture.require(toFail: screenEdgeGes)
}
}
}
}
}
#objc public func screenEdgePanAction(edgePan:UIScreenEdgePanGestureRecognizer){
let currenLocation = edgePan.location(in: self.view)
if let menuView = menuContainerView, JLSlideNavigationController.myMenuVC!.enabled{
if edgePan.state == UIGestureRecognizer.State.began{
panEnabled = false
if !menuIsPresented(){
panEnabled = true
lastPanTouch = currenLocation
}
}
else if edgePan.state == UIGestureRecognizer.State.ended{//at the end of pan gesture check if the menu is near to show or hide completelly and then finishes the movement
panEnabled = false
let menuFrame = menuView.frame
if comeFromLeft{
if menuFrame.origin.x <= -menuFrame.size.width/2{
self.hideMenu(animated: true)
}
else if menuFrame.origin.x > -menuFrame.size.width/2{
self.showMenu(animated: true)
}
}
else{
if menuFrame.origin.x >= self.view.frame.width - menuFrame.size.width/2{
self.hideMenu(animated: true)
}
else if menuFrame.origin.x < self.view.frame.width - menuFrame.size.width/2{
self.showMenu(animated: true)
}
}
}
if panEnabled{//if the pan started at a right point continue
menuView.alpha = 1
let xDeslocamento = currenLocation.x - lastPanTouch!.x
lastPanTouch = currenLocation
if comeFromLeft{
if menuView.frame.origin.x + xDeslocamento >= -menuView.frame.width && menuView.frame.origin.x + xDeslocamento <= 0{
menuView.frame.origin = CGPoint(x:menuView.frame.origin.x + xDeslocamento ,y:menuView.frame.origin.y)
}
}
else{
if menuView.frame.origin.x + xDeslocamento >= self.view.frame.width - menuView.frame.width && menuView.frame.origin.x + xDeslocamento <= self.view.frame.width{
menuView.frame.origin = CGPoint(x:menuView.frame.origin.x + xDeslocamento ,y: menuView.frame.origin.y)
}
}
}
}
}
}

How can I disable a UIButton until multiple TextFields are filled in with data (numbers)?

I have 3 UITextFields in my app and I need a user to fill in before he/she can see the UIButton which will calculate the result. I have tried a lot of different methods to resolve the issue but they don't seem to work and I can't quite understand some of them. I am sure there is a simple way to do that. P.S.: I don't use a storyboard
I have already tried to include UITextFieldDelegate, then writing a function, also used other ways, etc...
import UIKit
let Label: UILabel = {
let f = UILabel()
f.text = "Label"
return f
}()
let textFiled1: UITextField = {
let fw = UITextField()
fw.keyboardType = UIKeyboardType.decimalPad
return fw
}()
let textField2: UITextField = {
let fh = UITextField()
fh.keyboardType = UIKeyboardType.decimalPad
return fh
}()
let textField3: UITextField = {
let fa = UITextField()
fa.keyboardType = UIKeyboardType.numberPad
return fa
}()
class ViewController: UIViewController {
let calculateButton: UIButton = {
let c = UIButton()
c.setTitle("Calculate", for: .normal)
return c
}()
override func viewDidLoad() {
super.viewDidLoad()
setupLabel()
setupTextFieldComponents()
setupCalculateButton()
}
fileprivate func setupLabel() {
view.addSubview(fLabel)
}
fileprivate func setupTextFieldComponents() {
setupTextField1()
setupTextField2()
setupTexrField3()
}
fileprivate func setupTextField1() {
view.addSubview(textField1)
}
fileprivate func setupTextField2() {
view.addSubview(textField2)
}
fileprivate func setupTextField3() {
view.addSubview(textField3)
}
fileprivate func setupCalculateButton() {
view.addSubview(calculateButton)
}
#objc func calculateButtonPressed(sender: UIButton){
let textfield1 = Float(textField1.text!)
let textfield2 = Float(textField2.text!)
let textfield3 = Float(textField3.text!)
femaleMetaLabel.text = String(111 + (1.1 * textfield1!) + (1.1 * textfield2!) - (1.1 * textfield3!))
}
}
User is supposed to enter numeric data in all three text fields in order to calculate the result. Is there a way to make an ERROR message pop out without an app crashing?
When I am doing validation on text input that enables a button, I usually do something like this:
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
submitButton.isEnabled = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self,
selector: #selector(validateInput(_:)),
name: UITextField.textDidChangeNotification,
object: usernameField)
NotificationCenter.default.addObserver(self,
selector: #selector(validateInput(_:)),
name: UITextField.textDidChangeNotification,
object: passwordField)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
#objc func validateInput(_ sender: Any?) {
submitButton.isEnabled = false
if let username = usernameField.text,
let password = passwordField.text {
// your validation rules will be more complex than this :D
if username.count > 0 && password.count > 0 {
submitButton.isEnabled = true
}
}
}
If what you need to do is pop an alert when the inputs are incorrect, simply substitute a call to a method like the one below, where you vaildate the user's input.
func showAlert() {
let alert = UIAlertController(title: "Nope",
message: "Your inputs were incorrect.",
preferredStyle: .alert)
alert.addAction( UIAlertAction(title: "Try again",
style: .default,
handler: nil) )
self.present(alert, animated: true, completion: nil)
}
#Maxim you are almost there. Let me add a simple delegate in your code to achieve your goal.
Add code in your textFieldDidEndEditing delegate -
func textFieldDidEndEditing(textField: UITextField) {
if (textField1.text?.count)! > 0 && (textField2.text?.count)! > 0 && (textField3.text?.count)! > 0 {
calculateButton.isEnabled = true
}else{
calculateButton.isEnabled = false
}
}
Now time to calculate the result:
#objc func calculateButtonPressed(sender: UIButton){
if let inputOne = Float(textField1.text!), let inputTwo = Float(textField2.text!), let inputThree = Float(textField3.text!) {
femaleMetaLabel.text = String(111+(1.1*inputOne)+(1.1*inputTwo)-(1.1*inputThree))
}else{
let alert = UIAlertController(title: "", message: "You entered invalid inputs", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true)
}
}
Hope it will help you. Let me know if you are still having any issue.

Cannot dynamically add custom cell from 2nd data source

I've spent 20+ hours searching/trying different solutions just for this specific problem and cannot make it work. I'm just starting to learn Swift, so be gentile... I made sure to exhaust all options before asking for help.
I have 3 view controllers (VC1: form with 5 text fields, VC2: tableview for displaying gathered data, VC3: 2nd form with 3 text fields and an image picker)
screen shot of storyboard
VC1 gathers the text field data, pass to VC2, and use custom cell #1 to add to the tableview. I used the exact same methods to do the same for VC3 (changing the cell identifier, and using if/else to determine which section to add to) but cannot get any results. At first I thought my data wasn't being passed, but that checks out ("Finihed & Send" button set to alert and prints variable's text) next I thought it was my logic, but custom cell #1 works... I've been staring it this code for so long, I have horrible dreams about it. I feel like this should be obtainable with the techniques I'm applying but wonder if I am I wandering into Core Data territory but just don't know it.
tableview with only 1 custom cell
My intent is to have VC1 add a cell (custom cell 1) at indexPath.row 0 and VC3 to add (custom cell 2) indexPath.row >= 1. Everything works like I want it to with the exception of adding a second custom cell to the tableview.
addWorkViewController
import UIKit
import MapKit
import CoreLocation
class mainVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
#IBOutlet weak var scrollviewWORK: UIScrollView!
#IBOutlet weak var typeWORK: UISegmentedControl!
#IBOutlet weak var locationWORK: UITextField!
#IBOutlet weak var positionWORK: UISegmentedControl!
#IBOutlet weak var priceWORK: UITextField!
#IBOutlet weak var photo1WORK: UIImageView!
#IBOutlet weak var descriptionWORK: UITextView!
/// Prepare Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case is WorkOverview:
let workDest: WorkOverview = segue.destination as! WorkOverview
var cost = ""
var snap = UIImage()
if (priceWORK.text == nil) {
cost = ""
} else {
cost = priceWORK.text!
}
if (photo1WORK.image == nil) {
snap = UIImage()
} else {
snap = photo1WORK.image!
}
workDest.workLocation = locationWORK.text!
workDest.workDescription = descriptionWORK.text!
workDest.workPrice = cost
workDest.workPhoto = snap
case is PlantList:
let plantDest: PlantList = segue.destination as! PlantList
plantDest.placeholder = ""
default:
break
}
}
/// END Segue Preparation
/// Save to List Button
#IBAction func saveToListBTN(_ sender: UIButton) {
performSegue(withIdentifier: "unwindToList", sender: self)
}
/// END Save to List Button
/// Insert Plant
#IBAction func insertPlant(_ sender: UIButton) {
performSegue(withIdentifier: "toPlantListSegue", sender: self)
}
var addedPlant: String? = ""
/// END Insert Plant
/// Clear All Button
#IBAction func clearAllBTN(_ sender: UIButton) {
}
/// END Clear All Button
/// Segmented Controller - Work Type
#IBAction func positionChanged(_ sender: UISegmentedControl) {
switch positionWORK.selectedSegmentIndex {
case 0:
locationWORK.text? += " - Front"
case 1:
locationWORK.text? += " - Back"
case 2:
locationWORK.text? += " - Side"
default:
break
}
}
#IBAction func indexChanged(_ sender: UISegmentedControl) {
switch typeWORK.selectedSegmentIndex {
case 0:
descriptionWORK.text = "Provide and install "
case 1:
descriptionWORK.text = "Replace "
case 2:
descriptionWORK.text = "Remove and dispose of "
default:
break
}
}
/// END Segmented Controller - Work Type
/// ScrollView Keyboard Adjust
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == priceWORK){
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 205), animated: true)
} else {}
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
/// END Scrollview Keyboard Adjust
/// VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
// Toolbar
let toolBar = UIToolbar()
toolBar.sizeToFit()
let backArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "backArrow"), style: .plain, target: nil, action: #selector(backArrowClicked))
let spacerA = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let workDoneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked))
let spacerB = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let nextArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "nextArrow"), style: .plain, target: nil, action: #selector(nextArrowClicked))
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
locationWORK.inputAccessoryView = toolBar
priceWORK.inputAccessoryView = toolBar
descriptionWORK.inputAccessoryView = toolBar
}
/// END VIEWDIDLOAD
/// Toolbar - Done Button
#objc func doneBtnClicked() {
view.endEditing(true)
}
/// END Toolbar - Done Button
/// Arrow to Next TextField
#objc func nextArrowClicked() {
if (locationWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
} else if (descriptionWORK.isFirstResponder) {
priceWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
/// END Arrow to Next TextField
/// Arrow to Previous TextField
#objc func backArrowClicked() {
if (locationWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
} else if (descriptionWORK.isFirstResponder) {
locationWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
}
}
/// END Arrow to Previous TextField
/// Image Select from Library & Camera
#IBAction func takePhotoONE(_ sender: UIButton) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Want to add a photo?", message: "Please choose a source.", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}else{
print("Camera is not available")
}
}))
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Remove Photo", style: .destructive, handler: { (action:UIAlertAction) in self.photo1WORK.image = nil}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
photo1WORK.image = image
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
/// END Image Select from Library & Camera
/// GPS Location
let addressManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let streetAddress = locations[0]
CLGeocoder().reverseGeocodeLocation(streetAddress) { (placemark, error) in
if error != nil
{
print ("Sorry, there has been an error.")
}
else
{
if let place = placemark?[0]
{
if place.subThoroughfare != nil
{
self.locationWORK.text = "\(place.subThoroughfare!) \(place.thoroughfare!)"
}
}
}
}
}
#IBAction func getGPS(_ sender: UIButton) {
// Address
addressManager.delegate = self
addressManager.desiredAccuracy = kCLLocationAccuracyBest
addressManager.requestWhenInUseAuthorization()
addressManager.startUpdatingLocation()
}
/// END GPS Location
}
TableViewController
import UIKit
class WorkOverview: UIViewController {
#IBOutlet weak var listTableView: UITableView!
#IBAction func addWorkBTN(_ sender: UIButton) {
performSegue(withIdentifier: "overviewToWorkSegue", sender: self)
}
#IBAction func unwindToList(segue:UIStoryboardSegue) { }
#IBAction func finishedBTN(_ sender: UIButton) {
let alertController = UIAlertController(title: "Data Pass Test", message:
workLocation, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
self.present(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
property = createArray()
workPJs = workArray()
listTableView.delegate = self
listTableView.dataSource = self
}
var propForm = String()
var propName = String()
var propCity = String()
var propDate = String()
var propDue = String()
var propRep = String()
var workLocation = String()
var workDescription = String()
var workPrice = String()
var workPhoto = UIImage()
var workPJs: [WorkManager] = []
var property: [TaskManager] = []
func workArray() -> [WorkManager] {
var tempWork: [WorkManager] = []
let work1 = WorkManager(location: workLocation, description: workDescription, price: workPrice, photo: workPhoto)
tempWork.append(work1)
return tempWork
}
func createArray() -> [TaskManager] {
var tempProperty: [TaskManager] = []
let prop1 = TaskManager(title: propForm, property: propName, city: propCity, date: propDate, due: propDue, rep: propRep)
tempProperty.append(prop1)
return tempProperty
}
}
extension WorkOverview: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return property.count
} else {
return workPJs.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let prop = property[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PropertyCell", for: indexPath) as! PropertyCell
cell.setProperty(prop: prop)
return cell
} else if indexPath.section == 1 {
let wrk = workPJs[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "WorkCell", for: indexPath) as! WorkCell
cell.setWork(wrk: wrk)
return cell
}
return UITableViewCell()
}
}
WorkCell class
import UIKit
class WorkCell: UITableViewCell {
#IBOutlet weak var theLocation: UILabel!
#IBOutlet weak var theDescription: UILabel!
#IBOutlet weak var thePrice: UILabel!
#IBOutlet weak var thePhoto: UIImageView!
func setWork(wrk: WorkManager) {
theLocation.text = wrk.location
theDescription.text = wrk.description
thePrice.text = wrk.price
thePhoto.image = wrk.photo
}
}
WorkManager class
import UIKit
class WorkManager {
var location: String
var description: String
var price: String
var photo: UIImage
init(location: String, description: String, price: String, photo: UIImage){
self.location = location
self.description = description
self.price = price
self.photo = photo
}
}
in ViewDidLoad add this code according to names of your cell
tableView.registerNib(UINib(nibName: "cell xib", bundle: nil), forCellReuseIdentifier: "cell name")
in your case you have to register both nib of your custom cell.
i have feelings your section if else condition is wrong. better to debug.
Here, you need to define the second TableViewCell class but in your code only WorkCell class define. so, i hope below link is help you.
UITableview with more than One Custom Cells with Swift
I changed the if statement in cellForRowAt func to a switch case (don't know if it helped or not) but I know adding .reloadData() to my tableview in the viewDidAppear func got it working. Still needs some debugging but that's life.

Move view when a specific textfield is selected swift

I am created a meme generator app to better learn Swift and Xcode. I am learning to move the view when the user interacts with a text field that would be obstructed by the keyboard. I have this functionality working, with one exception. The desired functionality is to have the view slide up when the user is entering text for the bottom textfield, bot the top. The view slides up regardless of the text field the user is interacting with.
How can I assign this functionality only to the bottom text field? Here is my source code:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var imagePickerView: UIImageView!
#IBOutlet weak var cameraButton: UIBarButtonItem!
#IBOutlet weak var topText: UITextField!
#IBOutlet weak var bottomText: UITextField!
let memeTextAttributes:[String:Any] = [
NSAttributedStringKey.strokeColor.rawValue: UIColor.black,
NSAttributedStringKey.foregroundColor.rawValue: UIColor.white,
NSAttributedStringKey.font.rawValue: UIFont(name: "HelveticaNeue-CondensedBlack", size: 30)!,
NSAttributedStringKey.strokeWidth.rawValue: -5.0]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribeToKeyboardNotifications()
}
override func viewDidLoad() {
super.viewDidLoad()
// Diable camer a button if camera ource isn't available
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
topText.delegate = self
bottomText.delegate = self
topText.textAlignment = .center
bottomText.textAlignment = .center
topText.defaultTextAttributes = memeTextAttributes
bottomText.defaultTextAttributes = memeTextAttributes
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
// MARK: Delegate Methods
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
imagePickerView.image = image
self.dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.topText.resignFirstResponder()
self.bottomText.resignFirstResponder()
return true
}
// MARK: Move the keyboard up when the bottom textfield is tapped
#objc func keyboardWillShow(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
// MARK: Move view down when keyboard is dismissed
#objc func keyboardWillHide(_ notification: Notification) {
view.frame.origin.y = 0
}
// MARK: IBActions
#IBAction func pickAnImageFromAlbum(_ sender: Any) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = .photoLibrary
present(pickerController, animated: true, completion: nil)
}
#IBAction func pickAnImageFromCamera(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
}
You can simply try this
//make a global textField to keep reference
var currentTappedTextField : UITextField?
//use this method to get tapped textField
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
currentTappedTextField = textField
return true
}
// now move view only when textfield is bottom
#objc func keyboardWillShow(_ notification:Notification) {
if(currentTappedTextField == bottomText){
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
}
Add view in scrollview.
Use
pod 'IQKeyboardManagerSwift'
It will automatically handle that. In app delegate write this code :
IQKeyboardManager.sharedManager().enable = true
IQKeyboardManager.sharedManager().keyboardDistanceFromTextField = 30.0
If you want for one textfield:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == toptextField {
IQKeyboardManager.sharedManager().enable = false
}
else {
IQKeyboardManager.sharedManager().enable = true
}
return true
}
Approach without using external framework:
Use a bottom constraint from the text field to the parent view.
Adjust the constant value based on whether the keyboard is shown or hidden.
Steps:
Create a bottom constraint from your text field to the parent view.
Set the constraint's constant to an initial desired value
Add store the constraint as a property in the view controller
Observe UIKeyboardDidShow notification and get the end frame of the keyboard. Use the negative height of the end frame as the bottom constraint's constant.
Similarly do the same in UIKeyboardWillHide and set the bottom constraint constant to the original constant value
You just need to observe Keyboard Notification in your viewDidLoad :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide),
name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
And declare selector methods to change your view constraint :
#objc
func keyboardWillShow(_ notification: Notification) {
if let keyboardHeight = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
yourViewBottomConstraint.constant = keyboardHeight.cgRectValue.height + constantHeight
UIView.animate(withDuration: 0.25, animations: {
self.view.layoutIfNeeded()
})
}
}
#objc
func keyboardWillHide() {
yourViewBottomConstraint.constant = constantHeight
UIView.animate(withDuration: 0.25, animations: {
self.view.layoutIfNeeded()
})
}
Just don't forget to implement UITextFieldDelegate :
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
}

Swift: UIBarButtonItem only calls action to display UIImagePickerController's interface by holding down the button

I been reading iOS Programming by Big Nerd Ranch and doing the work in chapter 15 about cameras when I encountered this problem.
The camera will only pop up if I hold down the camera button. I added a breakpoint at my takePicture function and noticed that tapping doesn’t even call the function but holding does. Both delegates are included in my controller so that shouldn’t be a problem. The button is a UIBarButtonItem is that connected to my takePicture func on my controller.
import UIKit
class DetailViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var nameField: CustomTextField!
#IBOutlet var serialNumberField: CustomTextField!
#IBOutlet var valueField: CustomTextField!
#IBOutlet var dateLabel: UILabel!
#IBOutlet var imageView: UIImageView!
#IBAction func backgroundTapped(_ sender: UITapGestureRecognizer) {
view.endEditing(true)
}
#IBAction func takePicture(_ sender: UIBarButtonItem) {
let imagePicker = UIImagePickerController()
// If the device has a camera, take a picture; otherwise, just pic from photo library
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePicker.sourceType = .camera
} else {
imagePicker.sourceType = .photoLibrary
}
imagePicker.delegate = self
// Place image picker on the screen
present(imagePicker, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case "changeDate"?:
let dateCreatedViewController = segue.destination as! DateCreatedViewController
dateCreatedViewController.item = item
default:
preconditionFailure("Unexpected segue identifier.")
}
}
var item: Item! {
didSet {
navigationItem.title = item.name
}
}
let numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter
}()
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
nameField.text = item.name
serialNumberField.text = item.serialNumber
valueField.text = numberFormatter.string(from: NSNumber(value: item.valueInDollars))
dateLabel.text = dateFormatter.string(from: item.dateCreated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Clear first responder
view.endEditing(true)
// "Save" changes to item
item.name = nameField.text ?? ""
item.serialNumber = serialNumberField.text
if let valueText = valueField.text, let value = numberFormatter.number(from: valueText) {
item.valueInDollars = value.intValue
} else {
item.valueInDollars = 0
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
The permissions for accessing the camera and library were also added. Does it take a while for the camera to load? Whats going on?
The problem is that your recognizer gesture event is affecting your button.
It is called first that the tap on the button.
First delete the recognizer gesture in your viewcontroller.
And consider using an extension for your viewcontrollers as follows:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
}
It looks like while connected to debugger UIImagePickerController takes longer to initialize, disconnect device from debugger and it should open faster. You can also try to put opening code into:
DispatchQueue.main.async {
}

Resources