Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge iPad Layout branch with master #8

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3566ab5
Starting to work on iPad layout
Sendeky Nov 1, 2023
fb6247b
added a top stack to iPad view, changed how ViewControllers load in S…
Sendeky Nov 1, 2023
1bb9371
tried adding RocketView to iPad version, will fix later
Sendeky Nov 2, 2023
05b30f9
fixed the RocketView for iPad
Sendeky Nov 5, 2023
11647dd
made font work in iPadView
Sendeky Nov 5, 2023
7ff8fd8
trying to clean up iPad code a bit, did some work on HumidityView for…
Sendeky Nov 5, 2023
dd26640
fixed some iPad layout issues, started adding HumidityView to iPad ma…
Sendeky Nov 6, 2023
f0a39a6
iPad minor changes
Sendeky Nov 6, 2023
3e37fc5
added hourly collection view to iPad
Sendeky Nov 8, 2023
4628f76
started adding sunsetView for iPad
Sendeky Nov 14, 2023
caff475
added UVView to iPad, reorganized a bit
Sendeky Nov 14, 2023
bae34a3
fixed some spacing issues for iPad views
Sendeky Nov 15, 2023
b670a7e
added dailyForecastView to iPad
Sendeky Nov 15, 2023
2b08e42
changed how dailyForecast is layed out in iPad, changed cells for iPa…
Sendeky Nov 21, 2023
736e3e4
still trying to fix dailyForecastView on iPad
Sendeky Nov 21, 2023
80fb06d
fixed dailyForecastView on iPad, need to stylize the cell
Sendeky Nov 22, 2023
d609159
trying to customize the dailyForecast cell on iPad
Sendeky Nov 23, 2023
d8309fd
updated iPad dailyForecast cell
Sendeky Nov 23, 2023
02c563f
fixed UVView constraints for iPad layout
Sendeky Nov 24, 2023
eb65ec0
added days to iPadDailyForecastCells, refactored code a bit, fixed so…
Sendeky Dec 13, 2023
7598e57
added hours to iPadHourlyCells
Sendeky Dec 13, 2023
3a34bb3
added time fort iPadHourlyForecast
Sendeky Dec 14, 2023
f7a4b56
added blur effect to iPadDailyForecastCells
Sendeky Dec 14, 2023
a5cb8d2
updated App version and minor fixes to iPad Version. iPad version rea…
Sendeky Dec 31, 2023
02cfd88
Update README.md
Sendeky Dec 31, 2023
56d4c5e
Update README.md
Sendeky Dec 31, 2023
f9a7bfb
Added a gradient progress view for the UVview, will add the number sh…
Sendeky Dec 17, 2024
3162a78
Merge branch 'iPad-Layout' of https://github.com/Sendeky/weatherkit-w…
Sendeky Dec 17, 2024
33eef33
added UVView progress bar to iPad
Sendeky Dec 17, 2024
6f8c432
added a scrollview, added refresh control stuff
Sendeky Dec 18, 2024
7d670df
trying to rewrite the iPad modules so that I can actually update the …
Sendeky Dec 20, 2024
e6ceeec
rewrote humidityView, added function to update values for it
Sendeky Dec 21, 2024
57734aa
changed SunsetView
Sendeky Dec 21, 2024
c3cb0fd
changed all UiViews, they are no longer extensions of iPadMainViewCon…
Sendeky Dec 21, 2024
b801b7a
trying to add a wind direction graphic
Sendeky Dec 24, 2024
ceefe4e
finished the wind graphic, redid the UVView for iPad, added 2 more pr…
Sendeky Dec 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading