I am using Xcode 6 with swift.
I am trying to pass an image / UIImage from my UITabBarController to one of the subviews (tabs). The Image itself is being passed to the UITabBarController using the loadImage function (which is in return called by another UIViewController during prepareForSegue).
//this is the tab bar controller
class DesignerTabBarController : UITabBarController{
var pickedImage : UIImage = UIImage() //empty image
var VC1: DesignerTabController = DesignerTabController.alloc()
func loadImage(passedImage: UIImage){
pickedImage = passedImage
println(pickedImage) // image is accessible from here
}
override func viewDidLoad() {
super.viewDidLoad()
println(pickedImage) //image is accessible from here
VC1.pickedImageView.image = pickedImage // this line gives "unexpectedly found nil when unwrapping optional"
}
}
//this is the `UIViewcontroller` I want display the image (inside pickedImageView)
class DesignerTabController : UIViewController{
#IBOutlet weak var pickedImageView: UIImageView!
}
I realize that my way of passing the image from the UITabBarController to the sub-view will not work, even though when there are no syntax errors displayed within Xcode (it suggested pickedImageView when I started typing so I think I am really close to the solution).
If it's not possible passing data from my UITabBarController to the (first) subview, how can I pass data directly to the first subview? As I explained above, I am passing the image from another view using prepareForSegue. Does prepareForSegue directly to the first view work in my case?
Thank you very much for any suggestions, I'll try them and get back with my findings.
Your tab bar controller has viewControllers property. Just iterate through it check which controller is kind of class you need.
func passImage(passedImage: UIImage)
{
if viewControllers!.isEmpty{
return
}
for index in 0...viewControllers!.count{
if let controller = viewControllers[index] as? DesignerTabController
controller.pickedImageView.image = passedImage
}
}
solved by using viewDidLayoutSubviews() instead of viewDidLoad()
see below code for my working solution:
class DesignerTabController : UIViewController{
#IBOutlet weak var pickedImageView: UIImageView!
#IBOutlet weak var labelTest: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
class DesignerTabBarController : UITabBarController{
var pickedImage : UIImage = UIImage()
func loadImage(passedImage: UIImage){
pickedImage = passedImage
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
if let controller = viewControllers![0] as? DesignerTabController{
controller.pickedImageView.image = pickedImage
}
}
}
Related
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
UPDATED:
I have designed custom tabBar using buttons. I have 3 tabs,
First tab has Messages icon, Second has Profile icon and Third has Photos icon. For third tab button, I have used uiCollectionView() where I need to set images.
For the Third tab's ViewController,there is one condition that I need to check, before changing the title of the first tab button. If messages JSON array is not empty then set "new message" title on the first tab button, else the Messages icon won't change.
There is one ParentTabViewController which has these 3 tabs, I have used uiView, where I change the content according to the tab buttons pressed. I tried to access the values of 3rd tab in ParentTabViewController by using delegate, but the delegate is always nil. I did like this:
class ParentTabViewController: UIViewController,MessageDelegateProtocol{
#IBOutlet weak var contentView: UIView!
#IBOutlet var tabBarButtons : [UIButton]!
#IBOutlet weak var firstTabButton: UIButton!
var MessageVC : UIViewController!
var ProfileVC : UIViewController!
var PhotosVC : UIViewController!
var viewControllers : [UIViewController]!
var message : String!
var selectedIndex:Int = 0
var photoVC = PhotosVC()
override func viewDidLoad() {
super.viewDidLoad()
photoVC.newMessageDelegate = self
let storyBoard = UIStoryboard(name:"Main", bundle:nil)
MessageVC = storyBoard.instantiateViewController(withIdentifier: "messagevc")
ProfileVC = storyBoard.instantiateViewController(withIdentifier: "profile")
PhotosVC = storyBoard.instantiateViewController(withIdentifier: "photos")
viewControllers = [MessageVC, ProfileVC, PhotosVC]
tabBarButtons[selectedIndex].isSelected = true
didPressTabs(tabBarButtons[selectedIndex])
}
#IBAction func didPressTabs(_ sender: UIButton)
{
let previousIndex = selectedIndex
selectedIndex = sender.tag
tabBarButtons[previousIndex].isSelected = false
let previousVC = viewControllers[previousIndex]
previousVC.willMove(toParentViewController: nil)
previousVC.removeFromParentViewController()
previousVC.view.removeFromSuperview()
sender.isSelected = true
let presentVC = viewControllers[selectedIndex]
addChildViewController(presentVC)
presentVC.view.frame = contentView.bounds
contentView.addSubview(presentVC.view)
presentVC.didMove(toParentViewController: self)
if selectedIndex == 2{ // this is what I thought of doing.Correct me if wrong.
// check the condition
// if messagesArray != nil
// set the first tab title "new message"
}
else{
// do not change the button image
}
}
func sendMessage(message : String)
{
self.message = message
print("message........", self.message, "\n\n")
}
}
Here is the View Controller for 3rd tab:
import UIKit
protocol MessageDelegateProtocol:class {
func sendMessage(message : String)
}
class PhotosVC: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate{
#IBOutlet weak var collectionView: UICollectionView!
var userMessageArray = [UserMessageClass]() // array of model class
var newMessage : String!
weak var newMessageDelegate : MessageDelegateProtocol?
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
loadData() // function to get json reponse
}
// implement collectionView delegate and dataSource methods
func getData(newMsg : UserMessageClass) //func to get values from model class
{
newMessage = newMsg.messageString // here I get the "new message" String
newMessageDelegate?.sendMessage(message: newMessage)
} enter code here
func loadData()
{
// get json response. And pass the payload to UserMessageClass using that class's array
userMessageArray.append(UserMessageClass(dict : jsonData))
var msgData = UserMessageClass(dict: jsonData)
getData(alarm: msgData)
}
}
I tried searching a lot about accessing tab buttons in another VC, but didn't find any nearby approach as such. Also I am not able to figure out why delegate is always nil. Suggestions or Help would be grateful. Many Thanks :)
The problem is the following line.
let firstTab = storyBoard.instantiateViewController(withIdentifier: "parentVC") as! ParentViewController
You are probably expecting it to give you the instance of ParentViewController which you have setup initially. However, it will give you the instance of a newly initiated ParentViewController which is not what you want.
To counter this problem you can either make use of a delegate or completion block defined which will be defined inside your ParentViewController class.
Update:
Try adding PhotosVC.newMessageDelegate = self under the line
PhotosVC = storyBoard.instantiateViewController(withIdentifier: "photos")
Also change var PhotosVC : UIViewController! to var photosVC: PhotosVC!
This should work now.
My question is, how can I make it so redDot and wCircle can be accessed from the Second viewController so they can become hidden or not hidden. They are not connected directly, but you can get to them with different viewControllers.
First viewController
class SecondViewController: UIViewController
{
#IBOutlet weak var redDot: UIImageView!
#IBOutlet weak var wCircle: UIImageView!
}
Second viewController
class ProgressViewController: UIViewController {
#IBOutlet weak var rDot: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
rDot.isUserInteractionEnabled = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(rdotimageTapped(tapGestureRecognizer:)))
rDot.addGestureRecognizer(tapGestureRecognizer)
view.bringSubview(toFront: rDot)
}
func rdotimageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
wcircle.isHidden = true
redDot.isHidden = false
view.layoutIfNeeded()
print("It worked")
}
Im not sure if this is a right way but what I would do is have a reference of UIImageView in my second VC and then set that to the imageView of my first VC.
So add these two in your "ProgressViewController"
var redDot: UIImageView?
var wCircle: UIImageView?
and then in your prepare for segue pass your current UIImageView.
let progressViewController = segue.destination as! ProgressViewController
progressViewController.redDot = self.redDot
progressViewController.redDot.wCircle = self.wCircle
You can achieve that by adding a new member to your second and third view controllers, this member is a closure:
var updateFirstViewControllerImageViews: (() -> Void)?
Then override prepare for segue method of your firstViewController, and set updateFirstViewControllerImageViews for each of your second and third view controller like this:
secondViewController.updateFirstViewControllerImageViews = {
// Update your image views here in the way you want!
}
Now, in your second and third view controllers you can use that closure wherever you want like this:
self.updateFirstViewControllerImageViews?()
I need to display the image pass in from the another ViewController
first,I have tested using only ImageView to display the image and it works as below:
Thanks. Your help is greatly appreciated.
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var TempImgView: UIImgeView!
var passInImg: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
TempImgView.image = passInImg!
}
When I use UIScrollView ( for scrolling the large passInImg) , below code not working
Probelm:
error : fatal error: unexpectedly found nil while unwrapping an
optional value
But I have tested the pass in image in the above code.
what need to be done with the scrollView?
Should be used in viewDidLoad?
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var myUIScrollView: UIScrollView!
var myImgView: UIImageView!
var passInImg: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
self.myUIScrollView.maximumZoomScale = 10.0
self.myUIScrollView.minimumZoomScale = 1.0
self.myUIScrollView.delegate = self
myImgView.image = passInImg!
self.myUIScrollView.addSubview(myImgView)
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return myImgView
}
First check if myUIScrollView is connected to an outlet in storyboard and always check before using an optional variable:
if let img = passInImg {
myImgView.image = img
}
The difference is the way you are declaring the UIImageView.
In the first case:
#IBOutlet weak var TempImgView: UIImageView!
TempImgView is an #IBOutlet and hence initialised whenever the storyboard view/xib is loaded.
But then when you are using the scrollview, you have declared the UIImageView as a class variable and not #IBOutlet.
var myImgView: UIImageView!
It has to initialised before you try to access it.
Try:
myImgView = UIImageView()
myImgView.image = passInImg!
Please initialize the myImgView with frame before set the image.
It return nil because the myImgView not initalize
myImgView = UIImageView(frame:CGRectMake(10, 50, 100, 300));
It works for me. Hope it will helps you.Thanks alot
depending on a few parameters I am trying to set an Image on the second VC, but somehow it always returns null??
this is what i do:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
println("going forth!")
println(self.myButton.selected)
let controller = segue.destinationViewController as? DetailVC
if myButton.selected == true {
var newImage = UIImage(named:"Test1")
var oldImage = controller?.imageView.image
println(oldImage)
oldImage = newImage
} else {
var newImage = UIImage(named:"Test2")
var oldImage = controller?.imageView.image
println(oldImage)
oldImage = newImage
}
} else {
println("roflcopter")
}
}
the detail VC basically has "nothing":
import UIKit
class DetailVC: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var testString:String!
#IBAction func goBacktoMain(segue: UIStoryboardSegue) {
println("going back")
}
override func viewDidLoad() {
super.viewDidLoad()
println("view Did Load")
println(testString)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
the second VC has a default image set via Storyboard but when I stop with a breakpoint #let controller it will tell me that imageview actually is null, does this only get set once the detail vc was shown once?
€dit: setting the string and printing it out on the second VC works perfectly fine
Where are you setting the image?
Inside the prepareForSegue the UIImageView IBOutlet will not have loaded. You have to declare an image variable in your DetailVC class like below and set that image in viewDidLoad.
class DetailVC
{
var image:UIImage? = nil
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad()
{
imageView.image = image
}
}
In your prepareSegue function, set
let controller = segue.destinationViewController as? DetailVC
controller?.image = //Your Image according to conditions.
Declare a UIImage variable in the destination segue, pass the image object to it.
On viewDidLoad on the destination object, assign the image to the imageView.Image property
Most probably the Outlet is not yet initialized