Reputation: 180
I'm a newbie programmer and trying to create a Single View weather application. My goal is editing dateLbl1 created in WeeklyWeatherViewController from HomeViewController.
I wrote my code like this :
let WeeklyWeatherView = WeeklyWeatherViewController()
...
WeeklyWeatherView.dateLbl1.text = nextDayShort[0].uppercased()
But the label gets "nil" value.
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
(nextDayShort is not nil, and I connected UILabel to Storyboard)
Searched the web for my issue and tried calling ViewController as :
var swipePage: WeeklyWeatherViewController = UIStoryboard(name: "main", bundle: nil).instantiateViewControllerWithIdentifier("MyStoryboard")
I get this error :
Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
I read about passing data between View Controllers but couldn't use them in my code at all. How can I fix this issue? Thank you for any help!
HomeViewController.swift
import UIKit
import Foundation
import CoreLocation
let DateView = DateViewController()
let WeatherView = WeatherViewController()
let WeeklyWeatherView = WeeklyWeatherViewController()
var currentHour : String?
var currentDay: String?
var nextDayShort = [String]()
var listCount = 0
class HomeViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet var backgroudView: UIView!
@IBOutlet weak var daysView: UIView!
@IBOutlet weak var timeView: UIView!
@IBOutlet weak var splashImage: UIImageView!
@IBOutlet weak var weatherView: UIView!
@IBOutlet weak var middleView: UIView!
@IBOutlet weak var rightView: UIView!
@IBOutlet weak var leftView: UIView!
@IBOutlet weak var middleHourBGView: UIView!
var locationManager: CLLocationManager?
override func viewDidAppear(_ animated: Bool) {
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestAlwaysAuthorization()
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager?.startUpdatingLocation()
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .restricted, .denied:
let alert = UIAlertController(title: "Locations servies are disabled!", message: "Please go to settings and allow location services or the application can't fetch weather data.", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
}
}
alert.addAction(settingsAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
@unknown default:
break
}
} else {
print("Location services are not enabled")
}
}
override func viewDidLoad() {
super.viewDidLoad()
styleUI()
}
private func styleUI(){
daysView.layer.cornerRadius = 10
daysView.layer.shadowOpacity = 0.5
daysView.layer.shadowOffset = .zero
daysView.layer.shadowRadius = 10
middleView.layer.cornerRadius = 25
middleHourBGView.layer.cornerRadius = 10
let now = Date()
let formatter = DateFormatter()
let hour = Calendar.current.component(.hour, from: Date())
formatter.timeZone = TimeZone.current
formatter.dateFormat = "MMMM dd,YYYY EEEE"
let dateString = formatter.string(from: now)
DateView.dateLbl?.text = dateString
currentHour = String(hour)
let formatter2 = DateFormatter()
for n in 1..<6 {
var dayComponent = DateComponents()
dayComponent.day = n
let theCalendar = Calendar.current
let nextDate = theCalendar.date(byAdding: dayComponent, to: Date())
formatter2.dateFormat = "EE"
let nextDaySh = formatter2.string(from: nextDate! as Date)
nextDayShort.append(nextDaySh)
}
DispatchQueue.main.async {
WeeklyWeatherView.dateLbl1.text = nextDayShort[0].uppercased()
WeeklyWeatherView.dateLbl2.text = nextDayShort[1].uppercased()
WeeklyWeatherView.dateLbl3.text = nextDayShort[2].uppercased()
WeeklyWeatherView.dateLbl4.text = nextDayShort[3].uppercased()
WeeklyWeatherView.dateLbl5.text = nextDayShort[4].uppercased()
}
if (hour > 5 && hour < 21) {
backgroudView.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
timeView.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
daysView.layer.shadowColor = #colorLiteral(red: 0.3333333433, green: 0.3333333433, blue: 0.3333333433, alpha: 1)
} else {
backgroudView.backgroundColor = #colorLiteral(red: 0.0862745098, green: 0.09411764706, blue: 0.3058823529, alpha: 1)
timeView.backgroundColor = #colorLiteral(red: 0.0862745098, green: 0.09411764706, blue: 0.3058823529, alpha: 1)
DateView.cityLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
DateView.dateLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
DateView.tempLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
daysView.layer.shadowColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
WeatherView.leftHourLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
WeatherView.leftTempLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
WeatherView.rightHourLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
WeatherView.rightTempLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
WeatherView.middleTempLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location:CLLocationCoordinate2D = manager.location!.coordinate
weather.getWeather(lat: location.latitude, lon: location.longitude) { weather in
DispatchQueue.main.async {
DateView.cityLbl.text = weather.city.name
DateView.tempLbl.text = "\(Int(round(weather.list[0].main.temp)) - 273)°"
WeeklyWeatherView.tempLbl1.text = "\(Int(round(weather.list[1].main.temp)) - 273)°"
WeeklyWeatherView.tempLbl2.text = "\(Int(round(weather.list[2].main.temp)) - 273)°"
WeeklyWeatherView.tempLbl3.text = "\(Int(round(weather.list[3].main.temp)) - 273)°"
WeeklyWeatherView.tempLbl4.text = "\(Int(round(weather.list[4].main.temp)) - 273)°"
WeeklyWeatherView.tempLbl5.text = "\(Int(round(weather.list[5].main.temp)) - 273)°"
self.splashImage.isHidden = true
}
}
}
}
WeeklyWeatherViewController.swift
import UIKit
import Foundation
let storyBoardEdit = StoryboardEditor()
let weather = WeatherAPI()
class WeeklyWeatherViewController: UIView {
@IBOutlet weak var dateLbl1: UILabel!
@IBOutlet weak var dateLbl2: UILabel!
@IBOutlet weak var dateLbl3: UILabel!
@IBOutlet weak var dateLbl4: UILabel!
@IBOutlet weak var dateLbl5: UILabel!
@IBOutlet weak var tempLbl1: UILabel!
@IBOutlet weak var tempLbl2: UILabel!
@IBOutlet weak var tempLbl3: UILabel!
@IBOutlet weak var tempLbl4: UILabel!
@IBOutlet weak var tempLbl5: UILabel!
@IBOutlet weak var weatherImg1: UIImageView!
@IBOutlet weak var weatherImg2: UIImageView!
@IBOutlet weak var weatherImg3: UIImageView!
@IBOutlet weak var weatherImg4: UIImageView!
@IBOutlet weak var weatherImg5: UIImageView!
func fillUI(){
dateLbl1.text = nil
var dayId = [String]()
for x in 1..<6 {
dayId.append("03d")
let dayIdInt = Int(dayId[x-1])!
let imageIDName = storyBoardEdit.editWeatherIcons(dayIDInt: dayIdInt)
switch x {
case 1:
DispatchQueue.main.async {
self.weatherImg1.image = UIImage(named: imageIDName)
}
case 2:
DispatchQueue.main.async {
self.weatherImg2.image = UIImage(named: imageIDName)
}
case 3:
DispatchQueue.main.async {
self.weatherImg3.image = UIImage(named: imageIDName)
}
case 4:
DispatchQueue.main.async {
self.weatherImg4.image = UIImage(named: imageIDName)
}
case 5:
DispatchQueue.main.async {
self.weatherImg5.image = UIImage(named: imageIDName)
}
default:
break
}
}
}
}
Upvotes: 0
Views: 307
Reputation: 403
Do this changes:
weatherView
to WeeklyWeatherViewController
and reset the outlet. You should get it in this way:@IBOutlet weak var weatherView: WeeklyWeatherViewController!
let WeeklyWeatherView = WeeklyWeatherViewController()
styleUI()
method replace the lines using WeeklyWeatherView
by this:DispatchQueue.main.async {
weeklyWeatherView.dateLbl1.text = nextDayShort[0].uppercased()
weeklyWeatherView.dateLbl2.text = nextDayShort[1].uppercased()
weeklyWeatherView.dateLbl3.text = nextDayShort[2].uppercased()
weeklyWeatherView.dateLbl4.text = nextDayShort[3].uppercased()
weeklyWeatherView.dateLbl5.text = nextDayShort[4].uppercased()
}
In your locationManager() function, replace the lines using WeeklyWeatherView
by this:
weeklyWeatherView.tempLbl1.text = "\Int(round(weather.list[1].main.temp)) - 273)º"
weeklyWeatherView.tempLbl2.text = "\Int(round(weather.list[2].main.temp)) - 273)º"
weeklyWeatherView.tempLbl3.text = "\Int(round(weather.list[3].main.temp)) - 273)º"
weeklyWeatherView.tempLbl4.text = "\Int(round(weather.list[4].main.temp)) - 273)º"
weeklyWeatherView.tempLbl5.text = "\Int(round(weather.list[5].main.temp)) - 273)º"
See that I'm using the newly created outlet in both cases, right?
Upvotes: 2
Reputation: 7207
The problem is the way you have initialized the WeeklyWeatherViewController
let WeeklyWeatherView = WeeklyWeatherViewController()
This view has an associated xib from where you have created the connection. Now When you are initializing your WeeklyWeatherViewController
class that time your xib view is not been initialized.
You need to load the nib file in the init
method in your WeeklyWeatherViewController
.
class WeeklyWeatherViewController: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
let contentView = Bundle.main.loadNibNamed("your_nib", owner: self, options: nil)?.first as? UIView
addSubview(contentView!)
// set constraints for contentView.
}
convenience init() {
let defaultFrame = //provide your default frame
self.init(frame: defaultFrame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
So the thing is when you have an associated xib file, in short when you are supplying your view from outside you need to initialize that external view and attach it to your class. That way your view will still remain the memory and you can access all the connected IBOutlet
.
Upvotes: 1