Swift 3 - UIView does not always display its content - ios

I have a UIViewController (Main Menu) that is displayed after my other UIViewController (Loading Screen) finishes downloading/syncing data from the web. My Main Menu is having problems as it doesn't always display all the UIButtons that are on the view when it shows up! There is an inconsistancy, 1 out of every 10 times the Main Menu loads, all the UIButtons will appear.
Here's how the view should look:
Here's how the view usually shows up:
I can put my finger where the UIButtons should be and move my finger away and they'll appear. I can also tap where they should be and my segues will still trigger, but the UIButtons dont just automatically show up. It looks like
I attempted to add a MainMenuView.setNeedsDisplay() to the ViewDidLoad function and it didn't help. I also created a test UIButton on the view that triggers a MainMenuView.setNeedsDisplay() command but the hidden UIButtons remain hidden.
If I leave the screen idle for 30 or so seconds, all the UIButtons will randomly appear without me having to manually make them appear.
The Main Menu view is normal and all UIButtons are visible if I segue to it from any part of the app aside from my Loading Screen.
Edit: Main Menu Code - (references are Strong)
import UIKit
import CoreData
class MainMenuViewController: UIViewController {
// MARK: References
#IBOutlet var btnGasManifolds: UIButton!
#IBOutlet var btnAirCompressors: UIButton!
#IBOutlet var btnVacuumPumps: UIButton!
#IBOutlet var btnUnitConversion: UIButton!
#IBOutlet var btnCFMCalculator: UIButton!
#IBOutlet var mainMenuView: UIView!
var userData = [NSManagedObject]()
// MARK: Functions
override func viewDidLoad() {
var name = String()
for duserData in userData {
name = duserData.value(forKey: "nameFull") as! String
}
print("Main Menu\n->\(name)")
backgroundSetup()
}
override func viewDidAppear(_ animated: Bool) {
mainMenuView.setNeedsDisplay()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnTest(_ sender: Any) {
mainMenuView.setNeedsDisplay()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "settingsSegue" {
let destination = segue.destination as! SettingsViewController
destination.userData = userData
}
}
// Background Setup
func backgroundSetup() {
let background = UIImage(named: "Data Screens")
var imageView : UIImageView!
imageView = UIImageView(frame: view.bounds)
imageView.contentMode = UIViewContentMode.scaleAspectFill
imageView.clipsToBounds = true
imageView.image = background
imageView.center = view.center
view.addSubview(imageView)
self.view.sendSubview(toBack: imageView)
}
}

The code which creates and shows the buttons after fetching the data from server needs to run on Main UI thread. If the code is running the background thread it will not properly display the UI elements and will take some time or manual scroll before showing the controls.
Run the code on Dispatch main queue.

dispatch_async(dispatch_get_main_queue(),{
//do some work
})
OR
Override one of the methods from ViewWillLayoutSubviews and ViewDidLayoutSubviews accordingly and run your code in these methods.

Related

Hiding bottom tab bar before master object select

Xcode 10.1
Swift 4.2
I am using Master-Detail project. I need to add a bottom tab bar within the detail view but I don't want it to be displayed until an object in the Master view is selected.
Right now i used the "Hidden" option under "Drawing" for the tab bar which hides it during the initial launch, but can't find a way to make it displayed after selecting the master object.
class DetailViewController: UIViewController {
#IBOutlet weak var detailHeaderLabel: UINavigationItem!
#IBOutlet weak var detailDescriptionLabel: UILabel!
func configureView() {
// Update the user interface for the detail item.
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
if let headerLabel = detailHeaderLabel {
headerLabel.title = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
configureView()
}
var detailItem: String? {
didSet {
// Update the view.
configureView()
}
}
}
You need to create the IBOutlet of the tabbar from storyboard and than change is hidden property
DispatchQueue.global(qos: .background).async {
// Background Thread
getObjectForMaster()
DispatchQueue.main.async {
tabBar.isHidden = false
}
}

How to display an image when button is pressed in iOS?

I am trying to display an UIImageView when a button is pressed. In my main story board, I have an ImageView(hidden) and an UIButton. How do I modify the ViewController to make that happen?
import UIKit
class ViewController: UIViewController {
#IBOutlet var ImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
ImageView.image = UIImage(named: "18.pic_hd.jpg")
}
}
This is what I currently have so far, but the program breaks and gives me Thread 1: signal SIGABRT. I am new to Xcode and Swift, so any direction is appreciated!
You should drag and drop UIButton from storyBoard to your code and use Action to have an IBAction in your viewController.
After that use imageView.isHidden = false to show the image and vice versa.
sample code:
import UIKit
class ViewController: UIViewController {
#IBOutlet var imageView: UIImageView!
#IBAction func someButton(_ sender: UIButton) {
// when button tapped this code will execute
imageView.isHidden = false
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.isHidden = true
}
}
use
ImageView.isHidden = true
to hide your image, and
ImageView.isHidden = false
to show it.
add an IBAction to your button to do this.

How to change the color of a UIlabel in the next ViewController, to the selected color in the current ViewController in Swift

I have two view controllers. In the first one i have label and 4 buttons representing a color, and a button called next. I want to accomplish that when a user selects a color in the first view controller and clicks next, that the label in my next viewController will automatically have that color.
This is what i have so far but i can't make it work.
import UIKit
class ViewController: UIViewController {
var backGroundColor = UIColor()
#IBOutlet var face: UILabel!
#IBAction func red(sender: AnyObject) {
face.backgroundColor = UIColor.redColor()
}
#IBAction func green(sender: AnyObject) {
face.backgroundColor = UIColor.greenColor()
}
#IBAction func blue(sender: AnyObject) {
face.backgroundColor = UIColor.blueColor()
}
#IBAction func yellow(sender: AnyObject) {
face.backgroundColor = UIColor.yellowColor()
}
#IBAction func next(sender: AnyObject) {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("face") as! Face2
vc.newFace.backgroundColor = self.face.backgroundColor
self.navigationController?.pushViewController(vc, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// My Face2 class:
import UIKit
class Face2: UIViewController {
var backGroundColor = UIColor()
#IBOutlet var newFace: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// var vc = ViewController()
self.newFace.backgroundColor = backGroundColor
}
}
Welcome to SO.
You are making a common design mistake.
Don't try to manipulate the second view controller's views directly. That's bad design, and often doesn't work.
Instead add a property/properties to your Face2 class.
In this case, a backgroundColor property is probably all you need.
Set that property in your next function, and then in your Face2 class's viewDidLoad method, use the color to set the background color of whatever views need to use the custom color. If you decide later that the set of views that need to change color is different, you can change the code for your Face2 class and the first view controller doesn't need to change at all.
You also need to get rid of vc2. That makes no sense. You create a new Face2 view controller vc with a call to instantiateViewControllerWithIdentifier. That's the one you should be configuring. The one you create in vc2 is never used for anything. It will get deallocated as soon as you exit the next() function.
my suggestion is to set a nsstring (string that you set when you press the color button) property to second view controller and in the second view controller view did load make a switch based on the passed string to set the background color

Simple button on second view controller crashing app/ Swift

I have a very simple app so far. Two view controllers. I've set up a new .swift file for the second view. On each view I have a button that when pressed, changes a label to say "Pressed". Pretty simple.
On the first view controller everything works as expected. However, on the second view controller the app crashes when I press the button. I've set up IBOutlets and actions for all appropriate parts.
Does anyone have any insight?
code:
import UIKit
class PlayViewController: UIViewController {
#IBOutlet weak var newCardButton: UIButton!
#IBOutlet weak var labelTest: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func newCardButtonPressed(sender: UIButton) {
self.labelTest.text = "Pressed"
}
}
Screenshot:
Screenshot after crash- http://i.imgur.com/CHt8kA5.png
I think you should change the sender part like this.
#IBAction func newCardButtonPressed(sender: AnyObject) {
self.labelText.text = "Pressed"
}
If your connections are not set properly your app also crash. Delete them and reconnect it.From Utilities/connections inspector.

memory leak when using images in Swift?

I have a very simple app with two viewControllers at the moment. I have a transition between them (with a segue of course) and both screens have a lot of images and buttons with images.
What I discovered was that there is a memory leak when you alternate between the screens. I am guessing, that somehow the images are loaded again each time you open the viewController again. Around 6MB is added each time and I am pretty sure that the images are less than 1MB in total. So maybe there is something else craving all the memory?
I have 6 image Views and I use the cross dissolve transition in the default modus. I wish I could copy some code here, but it is a big project and I would not know what code would be helpful.
So my question would be: what is causing this problem (and preferable how can I fix this)? Is it the images, or maybe the segues with the transition.
Any help would be really appreciated! Thanks!
--- Edit: Here's some bits of code with the images
// variables of the bottom menu bar
#IBOutlet weak var cameraButton: UIButton!
#IBOutlet weak var redoButton: UIButton!
#IBOutlet weak var saveButton: UIButton!
#IBOutlet weak var shareButton: UIButton!
#IBOutlet weak var overLay: UIImageView!
// this function lets the statusbar disappear
override func prefersStatusBarHidden() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
overLay.image = nil
// variables for accessing web images
let url = NSURL(string: "http://www.fteunen.com/app/overLay.png")
let urlRequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("Something happened i guess, not good!!")
} else {
//let image = UIImage(data: data)
self.overLay.image = UIImage(data: data)
}
})
}
And I don't know where I can find code from the segues. I just clicked on some options for that.
---- EDIT: The second viewController (the one with all the images) and the dismissViewController:
class ViewController2: UIViewController {
// variables of the bottom menu bar
#IBOutlet weak var cameraButton: UIButton!
#IBOutlet weak var redoButton: UIButton!
#IBOutlet weak var saveButton: UIButton!
#IBOutlet weak var shareButton: UIButton!
#IBOutlet weak var overLay: UIImageView!
// this function lets the statusbar disappear
override func prefersStatusBarHidden() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
overLay.image = nil
// variables for accessing web images
let url = NSURL(string: "http://www.fteunen.com/app/overLay.png")
let urlRequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("Something happened i guess, not good!!")
} else {
//let image = UIImage(data: data)
self.overLay.image = UIImage(data: data)
}
})
}
override func viewDidLayoutSubviews() {
//code
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func didTakePhoto(sender: AnyObject) {
//code
})
}
override func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?) {
dismissViewControllerAnimated(true) {
// Optional completion handler code here
}
}
#IBAction func redoPicture(sender: AnyObject) {
// code
})
}
You are leaking memory because you are continuously creating new view controllers which are all essentially piling on top of each other and downloading images. You should perform a segue and then dismiss that view controller to return to the previous view controller. In your second view controller where you are trying to segue back to the first controller call dismissViewControllerAnimated in either of the following ways:
dismissViewControllerAnimated(true, completion: nil)
or:
dismissViewControllerAnimated(true) {
// Optional completion handler code here
}
Memory Leak due to constantly creating new VC ontop of VC
Use Unwind.
In MainViewController
#IBAction unwindSegue(segue: UIStoryboardSegue, sender: UIStoryboardSegue){//any code you want executed upon return to mainVC}
In NewViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){//pass any information from New to Main here}
Then, in NewVC, simply control+drag from whichever UIButton you want to cause the unwind to NewVC's Exit symbol and select unwindSegue
*
*
NOTE: Also if you want the unwind to happen programmatically instead of from a Button. Control+drag from NewVC (yellow button) to exit(red-orange button)
, this will create an unwind segue under "Exit". Select this "Unwind segue" and in attributes inspector give it an identifier.
Now in NewVC create a function
func NameYourFunc(){performSegueWithIdentifier("TheIdentiferYouUsed", sender: self)}
and anywhere in your NewVC code when you want to perform this unwind simply call NameYourFunc()

Resources