Skip to content

Commit

Permalink
Merge pull request #9 from Sendeky/iPad-Layout
Browse files Browse the repository at this point in the history
added iPad layout!
  • Loading branch information
Sendeky authored Dec 24, 2024
2 parents 28d6c1c + ceefe4e commit d969f59
Show file tree
Hide file tree
Showing 22 changed files with 2,091 additions and 28 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## What It Is:
* An iOS Weather App that uses Apple's WeatherKit API

<div style="display: flex; justify-content: center;">
<img src="https://user-images.githubusercontent.com/83136978/210652066-0f5549b8-3f8c-48dc-967e-34bd6565400f.jpg" alt="Image 1" style="margin-right: 10px; height:420px;">
<img src="https://user-images.githubusercontent.com/83136978/210652073-e86a0af8-fbea-426b-8188-6ce3ca398d15.jpg" alt="Image 2" style="margin-right: 10px; height:420px;">
Expand All @@ -14,6 +14,8 @@
<img src="https://user-images.githubusercontent.com/83136978/210652091-f347f1d6-8d0e-4d1b-8935-cd41b4518c9a.jpg" alt="Image 5" style="margin-right: 10px; height:420px;">
</div>

---

### [App Store Link](https://apps.apple.com/us/app/stormylaunch-weather-app/id6444372213)

## Background Info:
Expand All @@ -28,8 +30,9 @@ Minimum iOS: 16.0
Dependencies: None
```

The iOS app is built mostly in UIKit, with some occasionsal SwiftUI elements
The WatchOS app is built entirelty in SwiftUI
The iOS app is built mostly in UIKit, with some occasionsal SwiftUI elements.
Also has support for iPad.
The WatchOS app is built entirelty in SwiftUI.


## Contributing
Expand Down
25 changes: 21 additions & 4 deletions programmatic-ui-weather-app/Delegates/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,27 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let windowScene = (scene as? UIWindowScene) else { return }

window = UIWindow(frame: UIScreen.main.bounds)
let home = TabBarViewController()
self.window?.rootViewController = TabBarViewController()
window?.windowScene = windowScene
window?.makeKeyAndVisible()

switch UIDevice.current.userInterfaceIdiom {
case .phone:
// It's an iPhone
print("iphone")
let home = TabBarViewController()
self.window?.rootViewController = TabBarViewController()
window?.windowScene = windowScene
window?.makeKeyAndVisible()
case .pad:
// It's an iPad (or macOS Catalyst)
print("ipad")
let home = iPadMainViewController()
self.window?.rootViewController = iPadMainViewController()
window?.windowScene = windowScene
window?.makeKeyAndVisible()

@unknown default:
// Uh, oh! What could it be?
print("oops, unknown device")
}
}

func sceneDidDisconnect(_ scene: UIScene) {
Expand Down
8 changes: 4 additions & 4 deletions programmatic-ui-weather-app/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.ES.refresh</string>
</array>
<key>UIAppFonts</key>
<array>
<string>SpaceX.ttf</string>
Expand Down Expand Up @@ -31,9 +35,5 @@
<array>
<string>fetch</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.ES.refresh</string>
</array>
</dict>
</plist>
4 changes: 4 additions & 0 deletions programmatic-ui-weather-app/Utils/WeatherKitCallUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extension MainViewController {

MF0.numberFormatter.maximumFractionDigits = 3
let uv = result.0.uvIndex.value
let uvCategory = result.0.uvIndex.category.description
let windSpeed = MF0.string(from: result.0.wind.speed)
print(windSpeed)
let windDirection = result.0.wind.compassDirection
Expand Down Expand Up @@ -143,6 +144,7 @@ extension MainViewController {
WeatherKitData.TempMax = tempMax
WeatherKitData.TempMin = tempMin
WeatherKitData.UV = uv
WeatherKitData.UVCategory = uvCategory
WeatherKitData.WindSpeed = windSpeed
WeatherKitData.WindDirection = "\(windDirection)"
WeatherKitData.Symbol = symbol
Expand Down Expand Up @@ -177,6 +179,7 @@ extension MainViewController {
}
}
//@MainActor runs after getweather Task completes
//@MainActor ensures all code runs on main dispatch thread
@MainActor
private func updateLabelsAfterAwait() {
ForecastListVC().forecastTableView.reloadData()
Expand All @@ -187,6 +190,7 @@ extension MainViewController {

let date = WeatherKitData.SunsetDate
let interval = WeatherKitData.SunsetDate.timeIntervalSinceReferenceDate - WeatherKitData.SunriseDate.timeIntervalSinceReferenceDate
print("interval time: \(interval)")

let rocketTimer = Timer(fireAt: date, interval: interval, target: self, selector: #selector(AnimateRocket), userInfo: nil, repeats: false)
RunLoop.main.add(rocketTimer, forMode: RunLoop.Mode.common)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class CustomCell: UICollectionViewCell {
private func setupUI() {
// bigger top stackview
topStack = UIStackView()
// topStack.applyBlurEffect(cornerRadius: 15)
// topStack.layer.cornerRadius = 15

// individual stackview
stackView = UIStackView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,34 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
let windTitleLabel = UILabel()
let windLabel = UILabel()

// uvView variables
let uvView = UIView()
let uvTitleLabel = UILabel()
private let uvProgressView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 4
view.clipsToBounds = true
view.backgroundColor = UIColor.systemGray2.withAlphaComponent(0.3) // Add this line
return view
}()

private let uvGradientLayer: CAGradientLayer = {
let layer = CAGradientLayer()
layer.colors = [
UIColor.systemGreen.cgColor,
UIColor.systemYellow.cgColor,
UIColor.systemOrange.cgColor,
UIColor.systemRed.cgColor,
UIColor.systemPurple.cgColor
]
layer.startPoint = CGPoint(x: 0, y: 0.5)
layer.endPoint = CGPoint(x: 1, y: 0.5)
layer.cornerRadius = 2 // Add this line to match the view's corner radius
return layer
}()


// collection view for hourly forecast
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout.init()) // need to have frame and layout for UICollectionView

Expand All @@ -50,7 +78,6 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
//Creates view for cloud at top
let uiView = UIView()


override func viewDidLoad() {
super.viewDidLoad()

Expand Down Expand Up @@ -209,7 +236,7 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
windView.layer.cornerRadius = 20
windView.isUserInteractionEnabled = true
windView.addGestureRecognizer(windTapGesture)
// windView.applyBlurEffect(.systemUltraThinMaterialLight, cornerRadius: 20)
// windView.applyBlurEffect(.systemUltraThinMaterialDark, cornerRadius: 20)

windTitleLabel.translatesAutoresizingMaskIntoConstraints = false
windTitleLabel.text = "Wind"
Expand All @@ -219,6 +246,15 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
windLabel.text = "--MPH"
windLabel.font = .preferredFont(forTextStyle: .title1)

uvView.translatesAutoresizingMaskIntoConstraints = false
uvView.backgroundColor = cyanColor
uvView.layer.cornerRadius = 20
// uvView.applyBlurEffect(.systemUltraThinMaterialDark, cornerRadius: 20)

uvTitleLabel.translatesAutoresizingMaskIntoConstraints = false
uvTitleLabel.text = "UV"
uvTitleLabel.font = .preferredFont(forTextStyle: .body)

//Sets settings for refreshControl
refreshControl.translatesAutoresizingMaskIntoConstraints = false
refreshControl.attributedTitle = NSAttributedString("Fetching Weather")
Expand All @@ -236,6 +272,7 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
scrollView.addSubview(precipitationView)
scrollView.addSubview(hourlyForecastView)
scrollView.addSubview(windView)
scrollView.addSubview(uvView)
scrollView.addSubview(refreshControl)

//Adds elements into precipitationView
Expand All @@ -246,6 +283,12 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
windView.addSubview(windTitleLabel)
windView.addSubview(windLabel)

// Adds elements into uvView
uvView.addSubview(uvTitleLabel)
uvView.addSubview(uvProgressView)

uvProgressView.layer.addSublayer(uvGradientLayer)

//Adds elements into hourlyForecastView
hourlyForecastView.addSubview(hourlyForecastTitleLabel)
hourlyForecastView.addSubview(collectionView)
Expand Down Expand Up @@ -316,6 +359,19 @@ class MainViewController: UIViewController, CLLocationManagerDelegate, UIScrollV
//windLabel constraints
windLabel.centerYAnchor.constraint(equalTo: windView.centerYAnchor),
windLabel.centerXAnchor.constraint(equalTo: windView.centerXAnchor),
// uvView contraints
uvView.topAnchor.constraint(equalTo: windView.bottomAnchor, constant: 20),
uvView.leadingAnchor.constraint(equalTo: windView.leadingAnchor),
uvView.trailingAnchor.constraint(equalTo: windView.trailingAnchor),
uvView.heightAnchor.constraint(equalToConstant: 100),
// uvTitleLabel constraints
uvTitleLabel.topAnchor.constraint(equalTo: uvView.topAnchor, constant: 10),
uvTitleLabel.leadingAnchor.constraint(equalTo: uvView.leadingAnchor, constant: 10),
// uvProgressView constraints
uvProgressView.topAnchor.constraint(equalTo: uvTitleLabel.bottomAnchor, constant: 10),
uvProgressView.heightAnchor.constraint(equalToConstant: 6),
uvProgressView.leadingAnchor.constraint(equalTo: uvView.leadingAnchor, constant: 10),
uvProgressView.trailingAnchor.constraint(equalTo: uvView.trailingAnchor, constant: -10)
])//Constraint Array
}//Layout func
}//MainViewController class
Expand Down Expand Up @@ -379,6 +435,7 @@ extension MainViewController {
self.todayTempLabel.text = "H:\(WeatherKitData.TempMax) L:\(WeatherKitData.TempMin)"
self.windLabel.text = "\(WeatherKitData.WindSpeed)"
self.precipitationLabel.text = "\(WeatherKitData.PrecipitationChance)% Chance"
self.updateUVIndex(WeatherKitData.UV)
DateConverter().timeArrayMaker()
}

Expand Down Expand Up @@ -407,6 +464,34 @@ extension MainViewController {
fetchFromReload()
}

// Stuff so that uvGradientLayer frame works
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Update gradient layer frame
uvGradientLayer.frame = uvProgressView.bounds
}

// Function to update the UVIndex
func updateUVIndex(_ value: Int) {
print("skibb: \(WeatherKitData.UVCategory)")
// Create a mask layer to achieve the progress bar effect
let maskLayer = CALayer()
let clampedValue = min(max(value, 0), 11)
let percentage = CGFloat(clampedValue) / 11.0

// The gradient layer should always be full width
uvGradientLayer.frame = uvProgressView.bounds

// Create a mask that only shows a portion of the gradient
maskLayer.frame = CGRect(x: 0, y: 0,
width: uvProgressView.bounds.width * percentage,
height: uvProgressView.bounds.height)
maskLayer.backgroundColor = UIColor.white.cgColor

// Apply the mask to the gradient layer
uvGradientLayer.mask = maskLayer
}

//Creates a function for running fetchWeather from reload func
func fetchFromReload() {
DispatchQueue.main.async {
Expand Down Expand Up @@ -548,10 +633,12 @@ extension MainViewController {
}
}

// number of hourly cells
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}

// cell creator
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell

Expand All @@ -565,6 +652,7 @@ extension MainViewController {
}
} else { cell.weatherIcon.image = UIImage(systemName: "questionmark")}

// displays them only if more than 6 items in HourluForecast
if WeatherKitData.HourlyForecast.count > 6 {
cell.tempLabel.text = "\(Int((round(WeatherKitData.HourlyForecast[indexPath.row])*100)/100))˚"
} else { cell.tempLabel.text = "--" }
Expand All @@ -575,4 +663,5 @@ extension MainViewController {

return cell
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ struct WeatherKitData: Codable{
static var TempFeels = 0

static var UV = 0
static var UVCategory = ""

static var WindDirection = ""
static var WindDirectionAngle = 0.0
static var WindGusts = [Measurement<UnitSpeed>]()
static var WindSpeed = ""
static var WindSpeedForecast = [0.0]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// LaunchRocket.swift
// weatherkit-weather-app
//
// Created by Ruslan Spirkin on 11/4/23.
//
import UIKit
import Foundation


extension iPadMainViewController {
@objc func launchRocket() {
// rocketView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
UIImageView.animate(withDuration: 4.4, animations: {
[weak self] in
self!.rocketView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
self!.rocketView.frame.origin.y -= 1000
}) { (done) in
UIImageView.animate(withDuration: 2.0, delay: 8.0, options: [.curveEaseOut], animations: {
[weak self] in
self!.rocketView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
self!.rocketView.frame.origin.y += 1000
})
}
}
}
Loading

0 comments on commit d969f59

Please sign in to comment.