Adding View Programmatically from ViewController super class doesn't show in front - ios

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()
}

Related

Update subviews of UIView from ViewController

I have a UIViewController that implements a custom UIView, so;
override func loadView() {
view = CustomView()
}
The custom view has a few lables and buttons and all the normal stuff, problem is in my viewController I have a request, and when that request is done, I'd like to update some of those lables/buttons.
Right now, in my CustomView, I have functions, such as;
func updateView() {
labelOne.isHidden = true
LabelTwo.isHidden = false
}
So I call the appropriate function from my viewController when the request is done.
This works, but it feels wrong, is there a neater way to update the subviews of my custom UIView, from my viewController? Should I maybe be using protocols or delegates?
One thing I've found quite neat in the past is passing the model directly to the custom view, then using didSet to trigger updates.
class CustomView: UIView {
let labelOne = UILabel()
let labelTwo = UILabel()
var object:CustomObject! {
didSet {
self.labelOne.text = object.name
self.labelTwo.text = object.description
}
}
...
}
This means in your UIViewController you can do the request and then pass the model straight to the custom view.
RequestHelper.getObject() { object in
self.customView.object = object
}
Obviously here I'm guessing at your request and object names but hopefully you get the idea.

Add imageview to all viewcontrollers programmatically

there is a requirement like add an imageview to all viewcontrollers but I have 150+ xib's and it is time consuming to put imageview in every single xib.
Is there a common way to do it? I googled but nothing useful found.
Any help would be appreciated.
It will be easy if you use base class like this
class BaseVC: UIViewController
{
var imageView:UIImageView = UIImageView.init()
override func viewDidLoad() {
super.viewDidLoad()
addImageView()
}
override func viewWillAppear(_ animated: Bool) {
//self.view.bringSubview(toFront: imageView) //To bring imageview infront of other views put this method as per your requirement
}
func addImageView(name:String = "default")
{
let image = UIImage(named: name)
imageView.image = image
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
//view.addSubview(imageView)
view.insertSubview(imageView, at: 0) /*For put image view below all image*/
}
}
You need to derive all your view controller from this like this
class YourVC: BaseVC
also you can change the image with different viewcontrollers.
like
class YourVC: BaseVC
{
override func viewDidLoad() {
super.viewDidLoad()
addImageView(name:"xyz")
}
}
you can write an extension for UIView to add imageview into it and run it into every your viewcontrollers.
extension UIView {
func addImage() {
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: "Your Image Name")
addSubview(imageView)
imageView.didMoveToSuperview()
}
}
Create New ViewController without storyboard.
import UIKit
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setImageView()
}
func setImageView() {
let thisImageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 70, height: 70))
thisImageView.image = UIImage(named: "your image name")
view.addSubview(thisImageView)
}
}
now you can set this BaseViewController as delegate of your project's ViewControllers.
import UIKit
class MainViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
now you have this ImageView in MainViewController and all ViewControllers have BaseViewContoller as delegate.
Hope to be useful. Also sorry about my English.
TL;DR No. Based on my understanding of what you are imagining, no you can't write a function to add a UIImageView to every one of your viewControllers.
Long answer:
You need to create a separate controller swift file for each View and set it up in that file. You could create a supporting file in which you setup up the the ImageView then call in in the viewDidLoad for each view controller. (You could even make this an extension of UIView so you can just call something like self.setUpImageView())
My personal recommendation would be to drop the xibs as soon as you can and recreate everything pragmatically, I know it's a headache to throw all your work away but it is really worth it in the end on top of just being good practice. I have a file that I found that makes autolayout a breeze that I can share with you if you'd like. I used to really enjoy storyboards and xibs myself but they are a hassle that just isn't worth it anymore and cause such a headache in situation like this.

How to add property to UIButton?

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

Click button in a UIViewController that was loaded a subView (UIView)

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.

viewDidAppear & viewDidLoad Are Are Initializing Positions Too Late

I am looking to update a UIView thats in a storyboard (and instantiated from the storyboard) when it loads in the app.
I need to position a few icons in a dynamic way (that the interface builder doesn't let me do quite yet). However, if I put my code in viewDidAppear or viewDidLoad, it seems to be getting called too late.
Here is what happens the first few second or two when the view loads:
And then a bit later it goes to the right position (as the code was called).
My question is where do I need to initialize the positions of these objects so they dont snap over a second later. viewDidLoad and viewDidAppear are too late? Why!? :)
override func viewDidAppear(animated: Bool)
{
initView()
_gridLines = false
}
func initView()
{
_cameraFeed.initAfterLoad()
//center the buttons on half distance between middle button and screen edge
var middleDistance:CGFloat = _swapButton.frame.origin.x + _swapButton.frame.width/2
_linesButton.frame.origin.x = middleDistance/2 - _linesButton.frame.width/2
_flashButton.frame.origin.x = middleDistance + middleDistance/2 - _flashButton.frame.width/2
_selectPhotos.frame.origin.x = middleDistance/2 - _selectPhotos.frame.width/2
}
Swift and objc answers welcome!
Try putting the code setting the frame in viewWillAppear instead of viewDidAppear.
Also, you should call the super.
override func viewWillAppear(animated: Bool) {
initView()
_gridLines = false
super.viewWillAppear(animated)
}

Resources