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

Add paging to weather tab to allow user to see forecast for all favourite locations #5

Merged
merged 3 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/CI_iOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ jobs:

runs-on: macos-13

timeout-minutes: 10
timeout-minutes: 15

steps:
- uses: actions/checkout@v3

- name: Select Xcode
run: sudo xcode-select -switch /Applications/Xcode_14.3.app
run: sudo xcode-select -switch /Applications/Xcode_15.0.app

- name: Xcode version
run: /usr/bin/xcodebuild -version

- name: Build and test
run: xcodebuild clean build test -project WeatherApp.xcodeproj -scheme "CI_iOS" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" ONLY_ACTIVE_ARCH=YES
run: xcodebuild clean build test -project WeatherApp.xcodeproj -scheme "CI_iOS" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0" ONLY_ACTIVE_ARCH=YES
10 changes: 0 additions & 10 deletions .swiftlint.yml

This file was deleted.

45 changes: 45 additions & 0 deletions WeatherApp-iOS/Views/PageView/PageControl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// PageControl.swift
// WeatherApp-iOS
//
// Created by Alex Motoc on 01.09.2023.
//

import SwiftUI
import UIKit

struct PageControl: UIViewRepresentable {
let numberOfPages: Int
@Binding var currentPage: Int

func makeCoordinator() -> Coordinator {
Coordinator(control: self)
}

func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.numberOfPages = numberOfPages
control.addTarget(
context.coordinator,
action: #selector(Coordinator.updateCurrentPage(sender:)),
for: .valueChanged
)
return control
}

func updateUIView(_ uiView: UIPageControl, context: Context) {
uiView.currentPage = currentPage
}

class Coordinator: NSObject {
let control: PageControl

init(control: PageControl) {
self.control = control
}

@objc func updateCurrentPage(sender: UIPageControl) {
control.currentPage = sender.currentPage
}
}
}
21 changes: 21 additions & 0 deletions WeatherApp-iOS/Views/PageView/PageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// PageView.swift
// WeatherApp-iOS
//
// Created by Alex Motoc on 01.09.2023.
//

import SwiftUI

struct PageView<Page: View>: View {
let pages: [Page]
@State private var currentPage: Int = 0

var body: some View {
ZStack(alignment: .bottom) {
PageViewController(pages: pages, currentPage: $currentPage)
PageControl(numberOfPages: pages.count, currentPage: $currentPage)
.padding(.horizontal)
}
}
}
79 changes: 79 additions & 0 deletions WeatherApp-iOS/Views/PageView/PageViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// PageViewController.swift
// WeatherApp-iOS
//
// Created by Alex Motoc on 01.09.2023.
//

import SwiftUI
import UIKit

struct PageViewController<Page: View>: UIViewControllerRepresentable {

let pages: [Page]

@Binding var currentPage: Int

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

func makeUIViewController(context: Context) -> UIPageViewController {
let pageVC = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal
)
pageVC.dataSource = context.coordinator
pageVC.delegate = context.coordinator
return pageVC
}

func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
uiViewController.setViewControllers(
[context.coordinator.controllers[currentPage]],
direction: .forward,
animated: true
)
}

class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
let parent: PageViewController
let controllers: [UIViewController]

init(_ parent: PageViewController) {
self.parent = parent
self.controllers = parent.pages.map { UIHostingController(rootView: $0) }
}

func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController
) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else { return nil }
if index == 0 { return nil }
return controllers[index - 1]
}

func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController
) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else { return nil }
if index + 1 == controllers.count { return nil }
return controllers[index + 1]
}

func pageViewController(
_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool
) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = controllers.firstIndex(of: visibleViewController) {
parent.currentPage = index
}
}
}
}
40 changes: 25 additions & 15 deletions WeatherApp-iOS/Views/WeatherTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,18 @@ struct WeatherTab: View {

var body: some View {
if viewModel.isLocationPermissionGranted {
weatherContent
PageView(pages: makeWeatherPages(isLocationPermissionGranted: true))
.id(UUID())
.edgesIgnoringSafeArea(.top)
} else {
noLocationPermissionView
let weatherPages = makeWeatherPages(isLocationPermissionGranted: false).map { AnyView($0) }
let pages: [AnyView] = [AnyView(noLocationPermissionView)] + weatherPages
PageView(pages: pages)
.id(UUID())
.edgesIgnoringSafeArea(.top)
}
}

@ViewBuilder
var weatherContent: some View {
let weather = store.weatherInformation.first(where: { $0.isCurrentLocation })
WeatherView(weatherInfo: .init(
info: weather ?? viewModel.emptyWeather,
temperatureType: appSettings.temperatureType,
lastUpdated: viewModel.lastUpdated,
onRefresh: {
Task { await viewModel.getWeather() }
}
))
}

@ViewBuilder
var noLocationPermissionView: some View {
VStack(spacing: 20) {
Expand All @@ -43,4 +36,21 @@ struct WeatherTab: View {
}
.padding()
}

func makeWeatherPages(isLocationPermissionGranted: Bool) -> [WeatherView] {
var array = store.weatherInformation
if array.isEmpty && isLocationPermissionGranted {
array = [viewModel.emptyWeather]
}
return array.map {
WeatherView(weatherInfo: .init(
info: $0,
temperatureType: appSettings.temperatureType,
lastUpdated: viewModel.lastUpdated,
onRefresh: {
Task { await viewModel.getWeather() }
})
)
}
}
}
Loading