I'm trying to add a UIView subview into a UIViewController, and that UIView has a UISwitch that I want the user to be able to toggle. Based on the state, a UITextField's value will toggle back and forth. Here is the subview (InitialView):
import UIKit
class InitialView: UIView {
// All UI elements.
var yourZipCodeSwitch: UISwitch = UISwitch(frame: CGRectMake(UIScreen.mainScreen().bounds.width/2 + 90, UIScreen.mainScreen().bounds.height/2-115, 0, 0))
override func didMoveToSuperview() {
self.backgroundColor = UIColor.whiteColor()
yourZipCodeSwitch.setOn(true, animated: true)
yourZipCodeSwitch.addTarget(ViewController(), action: "yourZipCodeSwitchPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.addSubview(yourZipCodeSwitch)
}
}
If I want to have it's target properly pointing at the below function, where should I either set the target or include this function? I tried:
Setting the target in the UIViewController instead of the UIView
Keeping the function in the UIView
Here's the function:
// Enable/disable "Current Location" feature for Your Location.
func yourZipCodeSwitchPressed(sender: AnyObject) {
if yourZipCodeSwitch.on
{
yourTemp = yourZipCode.text
yourZipCode.text = "Current Location"
yourZipCode.enabled = false
}
else
{
yourZipCode.text = yourTemp
yourZipCode.enabled = true
}
}
And here is where I'm loading it into the UIViewController:
// add initial view
var initView : InitialView = InitialView()
// Execute on view load
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.addSubview(initView)
}
Any help is much appreciated - thanks!
Yeah, the didMoveToSuperView() placement doesn't make much sense. So you're creating a random, totally unconnected ViewController instance to make the compiler happy but your project sad. Control code goes in controllers, view code goes in views.
You need in your real ViewController:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(initView)
// Note 'self' is the UIViewController here, so we got the scoping right
initView.yourZipCodeSwitch.addTarget(self, action: "yourZipCodeSwitchPressed:", forControlEvents: .ValueChanged)
}
Also, .TouchUpInside is for UIButtons. Toggle switches are much more complicated, so their events are different. Touching up inside on a toggle switch's current setting can and should do nothing, whereas touchup inside on the opposite setting triggers the control event above. iOS does all the internal hit detection for you.
Related
I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.
I am really new to Swift and working on my first project (I have a bit of experience with Javascript and web development). I have run into trouble (going on 4 hours of trying different solutions).
I have an app where when a UIButton is pushed it logs a value to FireBase (ON). When it is pushed a second time it logs (OFF) to the database.
When I make the button change the view.backgroundColor and put if else tied to the colour it works.
But I can't for the life of me figure out how to build my if else based on the state of the button. I have now ended up trying to change the colour of the button itself and tie the if else to that. Which I know is a really messy improper way to go about it.
import UIKit
import Firebase
import FirebaseDatabase
class ViewController: UIViewController {
#IBAction func OnOffButton(_ sender: UITapGestureRecognizer){
if button.backgroundColor == UIColor.white {
OnOff(state:"ON")
OnOffButton.backgroundColor = UIColor.blue
} else{
OnOff(state:"OFF")
OnOffButton.backgroundColor = UIColor.white
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
OnOff(state: "Off")
OnOffButton.backgroundColor = UIColor.white
}
UIButton inherits from UIControl which has an isSelected property. Use this to track state. Typically you'll use button.isSelected == true to correspond to your on state, and false is your off state.
To toggle state you can use button.isSelected = !button.isSelected.
For your specific example:
// User pressed the button, so toggle the state:
button.isSelected = !button.isSelected
// Now do something with the new state
if button.isSelected {
// button is on, so ...
} else {
// button is off, so...
}
Here’s a quick and dirty implementation of Duncan C’s suggestion to use a buttonIsSelected variable to store the button state:
import UIKit
class ViewController: UIViewController {
var buttonIsSelected = false
#IBOutlet weak var onOffButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
updateOnOffButton()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func onOffButtonTapped(_ sender: Any) {
buttonIsSelected = !buttonIsSelected
updateOnOffButton()
}
func updateOnOffButton() {
if buttonIsSelected {
onOffButton.backgroundColor = UIColor.blue
}
else {
onOffButton.backgroundColor = UIColor.white
}
}
}
Par's solution should work. However, I would suggest a different approach.
In general it's not good design to store state in a view object. It's fragile, and doesn't follow the MVC design pattern, where view objects display and collect state information, not store it.
Instead I would create an instance variable in your view controller, buttonIsSelected. Toggle that when the button is tapped, and have it change the state of the button's selected property, the button's color, AND log the new state to FireBase.
If you store more complex state in your view controller it would be worth separating that out into a model object. It can be as simple as a struct that holds the different state values. That way you have a clear separation between your controller (view controller) and model.
thanks for all help:)! fixed it using iboutlet collection and add properies on viewDidLoad
I'm trying to add properties to keyboard keys like layer.shadowColor or layer.shadowRadius.
I got an error
'Value of type '(UIButton)' -> () has no member 'layer'
how to fix this ?
this is my code keyboardViewController.swift
import UIKit
class KeyboardViewController: UIInputViewController {
var newKeyboardView: UIView!
#IBAction func keyPressed(sender: UIButton) {
}
#IBOutlet var nextKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
loadInterface()
}
func loadInterface() {
// load the nib file
let keyboardNib = UINib(nibName: "newKeyboard", bundle: nil)
// instantiate the view
newKeyboardView = keyboardNib.instantiateWithOwner(self, options: nil)[0] as! UIView
// add the interface to the main view
view.addSubview(newKeyboardView)
// copy the background color
view.backgroundColor = newKeyboardView.backgroundColor
}
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
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal)
}
}
I think that in order to apply some style to the button, you need an outlet to this button.
Right now, from what I can understand, you are trying to apply styles to the button from the #IBAction to the sender, which is not the proper way to do it.
Try to make an outlet to the button in the view controller and then to apply the styles from within the viewDidLoad method.
I hope this is clear, but if you want a more specific answer you need to show us what you tried, for example pasting the code you have in the view controller
EDIT:
Based on the code you post, the keyboard is a Nib you instantiate from loadInterface(). I don't have a clear vision of the whole thing with only this piece of code, but it seems to me that you are trying to apply some styles to every key button of a keyboard view. Unfortunately this really depends on how the keyboard is implemented, can you provide some more details?
Anyway, from what I see I think you didn't write this code: probably you are following a tutorial or maintaining someone else's code. That's ok, but I suggest you to follow a an introduction course to iOS development with Swift, like the Udacity's one, which is fantastic IMHO (https://www.udacity.com/course/intro-to-ios-app-development-with-swift--ud585)
If you try to format your UIButton with QuartzCore framework, you'll need to import it first:
import QuartzCore
Then you will be able to access those members.
For example (latest swift3 code):
#IBAction func keyPressed(sender: UIButton) {
let button = sender as UIButton!
button?.backgroundColor = UIColor.red
button?.layer.shadowColor = UIColor.black.cgColor
button?.layer.shadowRadius = 1.0
button?.layer.cornerRadius = 4.0
}
In case you need to apply your styles sooner, try to consider to put this code into viewDidLoad or viewDidAppear methods:
self.nextKeyboardButton.backgroundColor = UIColor.red
self.nextKeyboardButton.layer.shadowColor = UIColor.black.cgColor
self.nextKeyboardButton.layer.shadowRadius = 1.0
self.nextKeyboardButton.layer.cornerRadius = 4.0
Seems like you're trying to "add property" not to a button, but rather to a closure which accepts a button as an argument.
Make it like this:
nextKeyboardButton.layer.shadowColor = UIColor.redColor.cgColor
nextKeyboardButton.layer.shadowRadius = 5.0
Here is my issue: I have a transparent UIBUtton hooked up to an action with a control event UIControlEventTouchDownRepeat. Behind this UIButton I have a UIView with the exact same frame as my UIButton, that has a UIGestureRecognizeradded to it.
My question is: Since both views have the same frame, is there a way to dynamically access one or the other depending on the user interaction.
i.e:
If the user double tap on the button, the button will be accessed, other wise, if no UIControlEventTouchDownRepeat is detected, the view behind it will be accessed.
Any ideas on how to proceed ?
Thanks guys
You can add 2 UITapGestureRecognizer to the view without a button and achieve what you are looking for.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let doubleTapGest:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "onTappedTwice")
doubleTapGest.numberOfTapsRequired = 2;
let singleTapGest:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "onTapped")
singleTapGest.numberOfTapsRequired = 1;
singleTapGest.requireGestureRecognizerToFail(doubleTapGest)
self.view.addGestureRecognizer(singleTapGest)
self.view.addGestureRecognizer(doubleTapGest)
}
func onTapped() {
NSLog("Tapped once")
}
func onTappedTwice() {
NSLog("Tapped twice")
}
I have a BaseViewController that my UIViewControllers extend so i can have explicit functions that i dont need to rewrite. Something i would like would be a functions such as self.showSpinner() and the viewController would show the spinner
My Code looks like this
class BaseViewController: UIViewController {
var actvIndicator : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
self.actvIndicator = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
self.actvIndicator.color = UIColor.blackColor()
self.actvIndicator.backgroundColor = UIColor.blackColor()
self.actvIndicator.frame = CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2, 100, 100);
self.actvIndicator.center = self.view.center
self.actvIndicator .startAnimating()
self.view.addSubview(self.actvIndicator)
self.actvIndicator.bringSubviewToFront(self.view)
self.edgesForExtendedLayout = UIRectEdge.None
self.navigationController?.navigationBar.translucent = false
}
func showSpinner(){
self.actvIndicator.startAnimating()
}
func hideSpinner(){
self.actvIndicator.stopAnimating()
}
}
And my viewcontrollers looks like this
class MyProjectViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.showSpinner()
}
}
MyProjectViewController have UITableView that fills the entire screen. When i set tblProjects.alpha = 0 i can see the spinner. But i want it in the front.
i also tried self.view.bringSubviewToFront(self.actvIndicator)
What am i missing?
A couple quick notes before I get into what I think your problem is:
When you add a subview it is automatically added to the top layer, no need for the bringSubviewToFront: in viewDidLoad: (which is being used wrong anyway).
You should not set view frames in viewDidLoad: (e.g. centering a view). Frames are not setup yet, so you should move that to viewWillAppear: or some other variant.
Now your issue is most likely a view hierarchy problem (further confirmed by your comment) and thus can probably be fixed by pushing the spinner to the front every time you want it to be shown, like:
func showSpinner() {
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}
The problem here stands on the fact that table view is draw after you are calling self.view.bringSubviewToFront(self.actvIndicator). A possible workaround for this is to call bringSubviewToFront when showing the spinner
func showSpinner(){
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}