Swift4 - Pagination - ios

I'm new in developing ios app. Still dont know much. I successfully can display the articles well from first page only but fail to show other articles from other page when I scroll-up to view more. I really need help if somebody knows how to do pagination from my collection View. Below is the picture of my api that I use.
And below is my code where i get the data from api -- (Artikel.swift)
import Foundation
import Alamofire
import SwiftyJSON
import os.log
struct Artikel: ServiceCompletionHandler {
static func getCategories (completionHandler: #escaping ArrayCompletionHandler) {
let headers: HTTPHeaders = [
"Accept": "application/json"
]
let parameters: Parameters = [
"secret_key": Config.api_key
]
Alamofire.request("\(Config.server_address)/article-categories", method: .post, parameters: parameters, headers: headers)
.responseJSON { (response) -> Void in
switch response.result {
case .success:
let json = JSON(response.result.value!)
if json["data"].exists() {
if let data = json["data"]["data"].array {
completionHandler(data, nil)
} else {
completionHandler([], nil)
}
} else {
completionHandler([], nil)
}
case .failure(let error):
print(error)
completionHandler([], "Network error has occured.")
}
}
}
static func getArticles(_ category_id: Int, completionHandler: #escaping ArrayCompletionHandler) {
let headers: HTTPHeaders = [
"Accept": "application/json",
"secret-key": Config.api_key]
let value: Int = category_id
let newcategory = String(describing: value)
// var nextpages = NewsSectionViewController.url
let new_api = Config.server_addforarticle + newcategory
Alamofire.request(new_api, method: .get, headers: headers)
.responseJSON { (response) -> Void in
switch response.result {
case .success:
let json = JSON(response.result.value!)
// dump(json, name: "testing")
if let articles = json["articles"]["data"].array {
completionHandler(articles, nil)
}else{
completionHandler([], nil)
}
case .failure(let error):
print(error)
completionHandler([], "Network error has occured.")
}
}
}
}
and here where i display my articles. But yeah, I cant do pagination it stuck to 4-5 articles only. I just dont have any idea how to do what I did in my android version.
NewsSectionViewController.swift
import UIKit
import SwiftyJSON
import AlamofireImage
protocol NewsSectionViewControllerDelegate {
func updateArrowPosition()
}
class NewsSectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: - Variables
var itemIndex: Int = 0
var category:JSON!
var stories:[JSON] = []
var delegate: NewsSectionViewControllerDelegate?
// MARK: - Views
#IBOutlet var titleLabel: UILabel!
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var loadingIndicator: UIActivityIndicatorView!
// MARK: - Constraint
#IBOutlet var titleTopConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.titleLabel.text = category["name"].stringValue.uppercased()
self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, 114, 0)
if #available(iOS 11.0, *) {
if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, 173, 0)
self.collectionView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 173, 0)
}
}
self.loadingIndicator.startAnimating()
Artikel.getArticles(category["id"].intValue) {
(articles, error) in
self.loadingIndicator.stopAnimating()
if error == nil {
self.stories = articles
self.collectionView.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Collection View
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.stories.count - 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell: NewsItemCollectionViewCell?
let item = stories[indexPath.row + 1]
if indexPath.row == 0 {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BigCell", for: indexPath) as? NewsItemCollectionViewCell
} else {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SmallCell", for: indexPath) as? NewsItemCollectionViewCell
}
cell!.titleLabel.text = item["title"].stringValue
if let thumbUrlString = item["banner_url_large"].string {
if let thumbUrl = URL(string: thumbUrlString) {
cell?.coverImageView.af_setImage(withURL: thumbUrl)
}
}
let wpDateFormatter = DateFormatter()
wpDateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dayDateFormatter = DateFormatter()
dayDateFormatter.dateStyle = .medium
dayDateFormatter.doesRelativeDateFormatting = true
let date = wpDateFormatter.date(from: item["date_publish_web"].stringValue)!
cell!.timestampLabel.text = dayDateFormatter.string(from: date)
return cell!
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let story = self.stories[indexPath.row + 1]
let newsContents = self.storyboard?.instantiateViewController(withIdentifier: "NewsContent") as! NewsContentViewController
newsContents.story = story
newsContents.title = self.category["name"].string
self.navigationController?.pushViewController(newsContents, animated: true)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.row == 0 {
let width = UIScreen.main.bounds.width - 30
return CGSize(width: width, height: 385 - 20)
} else {
let width = (UIScreen.main.bounds.width - 45) / 2
return CGSize(width: width, height: 210)
}
}
// MARK: - ScrollView
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.collectionView {
self.titleTopConstraint.constant = 30 - scrollView.contentOffset.y
}
if let delegate = self.delegate {
delegate.updateArrowPosition()
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Any help I really appreciated and thank you in advance :)

In NewsSectionViewController:
1) add global variables:
a) var nextPageUrl: String? ;
b) let numberOfPagination = 5 (5 it's a number of items in one url page);
c) add variable hasMoreItems
var hasMoreItems: Bool {
get {
guard let count = nextPageUrl?.count, count > 0 else {
return false
}
return true
}
}
d) add variable numberOfItems
var numberOfItems: Int {
get {
return secrets.count
}
}
2) change this line self.stories = articles to self.stories += articles
3) add function for pagination:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if (indexPath.item + 1 == numberOfItems) && ((indexPath.item + 1) % numberOfPagination == 0) {
if self.presenter.hasMoreItems {
self.loadData()
}
}
}
f) add func loadData()
func loadData() {
Artikel.getArticles(category["id"].intValue, nextPageUrl: nextPageUrl) {
(articles, error) in
self.loadingIndicator.stopAnimating()
if error == nil {
self.stories = articles
self.collectionView.reloadData()
}
}
}
In Artikel:
1) change getArticles function
static func getArticles(_ category_id: Int, nextPageUrl: String?, completionHandler: #escaping ArrayCompletionHandler) {
//...
let new_api = nextPageUrl ?? Config.server_addforarticle + newcategory
//...
}
2) in this function you can return nextPageUrl value in ArrayCompletionHandler

Related

How do you present images in a collection view in a table view?

I have a UICollectionView placed inside a UITableViewCell. The collection view has its scroll direction set to horizontal. I have set the collection view and the collection view cell in the table view right, but when I run it the images don't show up and as I see that data doesn't pass right from table view cell to collection view cell. I cannot find anything wrong. I hope someone can find the mistake with a clearer mind.
Imagine that I want the first cell to have a collection view of images horizontally and in the other cells rows of names for example.
You can see my project on this GitHub account: https://github.com/BenSeferidis/Nft-Assets/tree/v5 for better understanding.
Lobby View Controller (Main VC):
The AssetTableViewCell is another custom cell that generates the rest of the data from my models: NFT API
import UIKit
class LobbyViewController: UIViewController {
// MARK: - IBProperties
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var data: [DataEnum] = []
var likes:[Int] = []
var numlikes: Int = 0
var nfts: [Nft] = []
let creators : [Creator] = []
var icons: [Icon] = []
var loadData = APICaller()
// MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "AssetTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "AssetTableViewCell")
let nib2 = UINib(nibName: "CreatorsTableViewCell", bundle: nil)
tableView.register(nib2, forCellReuseIdentifier: "CreatorsTableViewCell")
tableView.dataSource = self //method to generate cells,header and footer before they are displaying
tableView.delegate = self //method to provide information about these cells, header and footer ....
downloadJSON {
self.tableView.reloadData()
print("success")
}
loadData.downloadData { (result) in
print(result)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentViewController {
destination.nft = nfts[tableView.indexPathForSelectedRow!.row]
destination.delegate = self
}
}
// MARK: - Methods
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://public.arx.net/~chris2/nfts.json")
URLSession.shared.dataTask(with: url!) { [self] data, response, error in
if error == nil {
do {
self.nfts = try JSONDecoder().decode([Nft].self, from: data!)
let creators = nfts.map { nft in
nft.creator
}
self.data.append(.type1(creators: creators))
self.nfts.forEach { nft in
self.data.append(.type2(nft: nft))
}
DispatchQueue.main.async {
completed()
}
}
catch {
print("error fetching data from api")
}
}
}.resume()
}
}
// MARK: - Extensions
extension LobbyViewController : UITableViewDelegate , UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
indexPath.row == 0 ? 200 : UITableView.automaticDimension
}
//gemizo ta rows tou table
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.data[indexPath.item] {
case .type1(let creators):
print("--->", creators)
let cell = tableView.dequeueReusableCell(withIdentifier: "CreatorsTableViewCell",
for: indexPath) as! CreatorsTableViewCell
cell.updateCreators(creators)
return cell
case .type2(let nft):
let cell = tableView.dequeueReusableCell(withIdentifier: "AssetTableViewCell",
for: indexPath) as! AssetTableViewCell
cell.nameLabel?.text = nft.name
cell.nameLabel.layer.cornerRadius = cell.nameLabel.frame.height/2
cell.likesLabel?.text = "\((numlikes))"
let imgUrl = (nft.image_url)
print(imgUrl)
cell.iconView.downloaded(from: imgUrl)
cell.iconView.layer.cornerRadius = cell.iconView.frame.height/2
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
}
extension LobbyViewController : TestDelegate{
func sendBackTheLikess(int: Int) {
numlikes = int
tableView.reloadData()
}
}
// MARK: - Enums
enum DataEnum {
case type1(creators: [Creator])
case type2(nft: Nft)
}
// MARK: - Struct
struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
Creators TableView Cell :
import UIKit
class CreatorsTableViewCell: UITableViewCell {
//MARK: - IBProtperties
#IBOutlet var creatorsCollectionView: UICollectionView!
//MARK: - Properties
var nft : Nft?
var creators : [Creator] = []
weak var delegate : CreatorsTableViewCellDelegate?
//MARK: - Life Cyrcle
override func awakeFromNib() {
super.awakeFromNib()
creatorsCollectionView.dataSource = self
creatorsCollectionView.delegate = self
let nibName = UINib(nibName: "CollectionViewCell", bundle: nil)
creatorsCollectionView.register(nibName, forCellWithReuseIdentifier: "CollectionViewCell")
}
func updateCreators( _ creators: [Creator]) {
self.creators = creators
}
required init?(coder aDecoder : NSCoder) {
super.init(coder: aDecoder)
}
}
//MARK: - Extensions
extension CreatorsTableViewCell : UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
CGSize(width: 30, height: 30)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return creators.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = creatorsCollectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell",
for: indexPath) as! CollectionViewCell
cell.renewCreators(creators)
cell.creatorName.text = creators[indexPath.row].user.username
cell.creatorName.layer.cornerRadius = cell.creatorName.frame.height/2
cell.creatorsImg.image = UIImage(named: creators[indexPath.row].profileImgURL )
cell.creatorsImg.layer.cornerRadius = cell.creatorsImg.frame.height/2
return cell
// cell.backgroundColor = .brown
// cell.creatorName?.text = creators[indexPath.row].user.username
// let imgUrl = (creators[indexPath.row].profileImgURL)
// print(imgUrl)
// cell.creatorsImg.downloaded(from: imgUrl)
// return cell
}
}
//MARK: - Protocols
protocol CreatorsTableViewCellDelegate: AnyObject {
func didSelectPhoto(index: Int)
}
CollectionViewCell:
import UIKit
class CollectionViewCell: UICollectionViewCell {
//MARK: - IBProperties
#IBOutlet var creatorsImg: UIImageView!{
didSet {
creatorsImg.contentMode = .scaleAspectFit
}
}
#IBOutlet var creatorName: UILabel!
//MARK: - Properties
var nft : Nft?
var creators : [Creator] = []
//MARK: - Life Cyrcle
override func awakeFromNib() {
super.awakeFromNib()
print(creators)
creatorName.backgroundColor = .systemCyan
creatorsImg.layoutIfNeeded()
creatorsImg.layer.cornerRadius = creatorsImg.frame.height / 2
}
func setUpCollectionViewCell(_ nft: Nft) {
}
func renewCreators( _ creators: [Creator]) {
self.creators = creators
}
}
//MARK: - Protocols
protocol CollectionViewCellDelegate: AnyObject {
func didSelectPhoto(index: Int)
}
Models:
import Foundation
// MARK: - Nft
struct Nft: Codable{
let id:Int
let image_url:String
let name:String
let creator: Creator
}
// MARK: - Icon
struct Icon:Codable{
let image_url:String
}
// MARK: - Creator
struct Creator: Codable {
let user: User
let profileImgURL: String
enum CodingKeys: String, CodingKey {
case user
case profileImgURL = "profile_img_url"
}
}
// MARK: - User
struct User: Codable {
let username: String?
}
APICaller :
import Foundation
final class APICaller {
static let shared = APICaller()
public struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
public func downloadData(completion:#escaping (Result<[Nft], Error>) -> Void )
{
guard let url = URL(string:Constants.url)else{
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
//print(response)
print("here")
guard let data = data , error == nil else{
print("something went wrong")
return
}
print("here4")
//mexri edo exoume parei ta data kai tora me to do-catch tha ta kanoume convert se object
do{
//Decode the response
let nfts = try JSONDecoder().decode([Nft].self, from: data)
completion(.success(nfts))
print(nfts)
}catch{
completion(.failure(error))
}
}
task.resume()
}
}

Why are 2 network calls using the same search string required before the UICollectionView decides there is data that needs to be refreshed?

I have a strange problem in a UIViewController that the user uses to search for a GIF.
There are essentially 2 issues:
The user has to enter the same search term twice before the UICollectionView triggers the cellForRowAt data source method after a call is made to reloadData().
After you enter the search term the first time, heightChanged() is called, but the self.GIFCollectionView.collectionViewLayout.collectionViewContentSize.height comes back as 0 even though I've confirmed that data is being received back from the server. The second time you enter the search term, the height is a non-zero value and the collection view shows the cells.
Here is an example of how I have to get the data to show up:
Launch app, go this UIViewController
Enter a search term (ie. "baseball")
Nothing shows up (even though reloadData() was called and new data is in the view model.)
Delete a character from the search term (ie. "basebal")
Type in the missing character (ie. "baseball")
The UICollectionView refreshes via a call to reloadData() and then calls cellForRowAt:.
Here is the entire View Controller:
import UIKit
protocol POGIFSelectViewControllerDelegate: AnyObject {
func collectionViewHeightDidChange(_ height: CGFloat)
func didSelectGIF(_ selectedGIFURL: POGIFURLs)
}
class POGIFSelectViewController: UIViewController {
//MARK: - Constants
private enum Constants {
static let POGIFCollectionViewCellIdentifier: String = "POGIFCollectionViewCell"
static let verticalPadding: CGFloat = 16
static let searchBarHeight: CGFloat = 40
static let searchLabelHeight: CGFloat = 24
static let activityIndicatorTopSpacing: CGFloat = 10
static let gifLoadDuration: Double = 0.2
static let gifStandardFPS: Double = 1/30
static let gifMaxDuration: Double = 5.0
}
//MARK: - Localized Strings
let localizedSearchGIFs = PALocalizedStringFromTable("RECOGNITION_IMAGE_SELECTION_GIF_BODY_TITLE", table: "Recognition-Osiris", comment: "Search GIFs") as String
//MARK: - Properties
var viewModel: POGIFSearchViewModel?
var activityIndicator = MDCActivityIndicator()
var gifLayout = PAGiphyCellLayout()
var selectedGIF: POGIFURLs?
//MARK: - IBOutlet
#IBOutlet weak var GIFCollectionView: UICollectionView!
#IBOutlet weak var searchGIFLabel: UILabel! {
didSet {
self.searchGIFLabel.text = self.localizedSearchGIFs
}
}
#IBOutlet weak var searchField: POSearchField! {
didSet {
self.searchField.delegate = self
}
}
#IBOutlet weak var activityIndicatorContainer: UIView!
//MARK: - Delegate
weak var delegate: POGIFSelectViewControllerDelegate?
//MARK: - View Lifecycle Methods
override func viewDidLoad() {
super.viewDidLoad()
self.setupActivityIndicator(activityIndicator: self.activityIndicator, activityIndicatorContainer: self.activityIndicatorContainer)
self.viewModel = POGIFSearchViewModel(data: PAData.sharedInstance())
if let viewModel = self.viewModel {
viewModel.viewDelegate = self
viewModel.viewDidBeginLoading()
}
self.gifLayout.delegate = self
self.gifLayout.isAXPGifLayout = true;
self.GIFCollectionView.collectionViewLayout = self.gifLayout
self.GIFCollectionView.backgroundColor = .orange
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// This Patch is to fix a bug where GIF contentSize was not calculated correctly on first load.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
self.viewModel?.viewDidBeginLoading()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.heightChanged()
}
//MARK: - Helper Methods
func heightChanged() {
guard let delegate = self.delegate else { return }
let height = self.GIFCollectionView.collectionViewLayout.collectionViewContentSize.height + Constants.verticalPadding * 3 + Constants.searchLabelHeight + Constants.searchBarHeight + activityIndicatorContainer.frame.size.height + Constants.activityIndicatorTopSpacing
print("**** Items in Collection View -> self.viewModel?.gifModel.items.count: \(self.viewModel?.gifModel.items.count)")
print("**** self.GIFCollectionView.collectionViewLayout.collectionViewContentSize.height: \(self.GIFCollectionView.collectionViewLayout.collectionViewContentSize.height); height: \(height)")
delegate.collectionViewHeightDidChange(height)
}
func reloadCollectionView() {
self.GIFCollectionView.collectionViewLayout.invalidateLayout()
self.GIFCollectionView.reloadData()
self.GIFCollectionView.layoutIfNeeded()
self.heightChanged()
}
func imageAtIndexPath(_ indexPath: IndexPath) -> UIImage? {
guard let previewURL = self.viewModel?.gifModel.items[indexPath.row].previewGIFURL else { return nil }
var loadedImage: UIImage? = nil
let imageManager = SDWebImageManager.shared()
imageManager.loadImage(with: previewURL, options: .lowPriority, progress: nil) { (image: UIImage?, data: Data?, error: Error?, cacheType: SDImageCacheType, finished: Bool, imageURL: URL?) in
loadedImage = image
}
return loadedImage
}
func scrollViewDidScrollToBottom() {
guard let viewModel = self.viewModel else { return }
if viewModel.viewDidSearchMoreGIFs() {
self.activityIndicator.startAnimating()
} else {
self.activityIndicator.stopAnimating()
}
}
}
extension POGIFSelectViewController: POSearchFieldDelegate {
func searchFieldTextChanged(text: String?) {
guard let viewModel = self.viewModel else { return }
viewModel.viewDidSearchGIFs(withSearchTerm: text)
}
}
extension POGIFSelectViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("**** CELL FOR ROW AT -> self.viewModel?.gifModel.items.count: \(self.viewModel?.gifModel.items.count)")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.POGIFCollectionViewCellIdentifier, for: indexPath) as! POGIFCollectionViewCell
guard let previewURL = self.viewModel?.gifModel.items[indexPath.row].previewGIFURL else {
return cell
}
var cellState: POGIFCollectionViewCell.CellState = .dimmedState
if self.selectedGIF == nil {
cellState = .defaultState
} else if (self.selectedGIF?.previewGIFURL?.absoluteString == previewURL.absoluteString) {
cellState = .selectedState
}
cell.setupUI(withState: cellState, URL: previewURL) { [weak self] () in
UIView.animate(withDuration: Constants.gifLoadDuration) {
guard let weakSelf = self else { return }
weakSelf.GIFCollectionView.collectionViewLayout.invalidateLayout()
}
}
if cell.GIFPreviewImageView.animationDuration > Constants.gifMaxDuration {
cell.GIFPreviewImageView.animationDuration = Constants.gifMaxDuration
}
cell.backgroundColor = .green
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let viewModel = self.viewModel else { return 0 }
return viewModel.gifModel.items.count
}
}
extension POGIFSelectViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let selectedGIF = self.viewModel?.gifModel.items[indexPath.row],
let delegate = self.delegate else {
return
}
self.selectedGIF = selectedGIF
delegate.didSelectGIF(selectedGIF)
self.reloadCollectionView()
}
}
extension POGIFSelectViewController: POGIFSearchViewModelToViewProtocol {
func didFetchGIFsWithSuccess() {
self.activityIndicator.stopAnimating()
print("**** didFetchGIFsWithSuccess() -> about to reload collection view")
self.reloadCollectionView()
}
func didFetchGIFsWithError(_ error: Error!, request: PARequest!) {
self.activityIndicator.stopAnimating()
}
}
extension POGIFSelectViewController: PAGiphyLayoutCellDelegate {
func heightForCell(givenWidth cellWidth: CGFloat, at indexPath: IndexPath!) -> CGFloat {
guard let image = self.imageAtIndexPath(indexPath) else {
return 0
}
if (image.size.height < 1 || image.size.width < 1 || self.activityIndicator.isAnimating) {
return cellWidth
}
let scaleFactor = image.size.height / image.size.width
let imageViewToHighlightedViewSpacing: CGFloat = 4 // this number comes from 2 * highlightedViewBorderWidth from POGIFCollectionViewCell
return cellWidth * scaleFactor + imageViewToHighlightedViewSpacing
}
func heightForHeaderView() -> CGFloat {
return 0
}
}
You'll see that the heightChanged() method calls a delegate method. That method is in another UIViewController:
func collectionViewHeightDidChange(_ height: CGFloat) {
self.collectionViewHeightConstraint.constant = height
}
So, I can't figure out why I need to either delete a character from the search term and re-add it in order for the data to refresh even though the very first call populated the view model with new data.
It's bizarre. Please help.

Fetching JSON to ViewController and segue to 2nd ViewController

I have fetched and parsed my Data from API in JSON format to FirstViewController and want to segue to the SecondViewController with the data selected at the concrete person from FirstViewController. The Problem is that I have an API with such example URL: https://www.example.com/api/?action=persons&ln=en which gives me all persons in this format:
[
{
"p_id": "4107",
"p_name": "Name1 Surname1",
"p_role": "Role1",
"general_image": "/imagedb/persons/4107/main/1.jpg"
},{
"p_id": "1978",
"p_name": "Name2 Surname2",
"p_role": "Role2",
"general_image": "/imagedb/persons/1978/main/1.jpg"
}, {...
...}
]
I am showing all these persons in my FirstViewController in CollectionView, which is working correctly, with images, names, roles. But also I need to show my SecondViewController with the data selected in FirstVC. My API for personByID is like this URL: https://www.example.com/api/?action=person&ln=en&personId=1978 which gives me JSON Data in this format:
{
"p_id": "1978",
"p_category": "[2]",
"p_name": "Name2 Surname2",
"p_role": "Role2",
"p_short": null,
"p_text": "long text...",
"p_date_start": "1922.02.05",
"p_date_end": "",
"p_profile_image": "1",
"p_status": "1",
"p_lang": "en",
"general_image": "/imagedb/persons/1978/main/1.jpg",
"photos": [
{
"image_id": "5",
"p_id": "1978",
"lang": "en",
"text": "some text...",
"general": "/imagedb/persons/1978/5.jpg",
"thumbs": "/imagedb/persons/1978/thumb/5.jpg"
},
{
"image_id": "7",
"p_id": "1978",
"lang": "en",
"text": "some text...",
"general": "/imagedb/persons/1978/7.jpg",
"thumbs": "/imagedb/persons/1978/thumb/7.jpg"
}
]
}
This is my Person Struct:
struct Person {
let id: String
let name: String
let role: String
fileprivate let imageURLString: String
var imageURL: URL? {
return URL(string: "https://www.example.com\(imageURLString)")
}
}
extension Person: JSONDecodable {
init(_ decoder: JSONDecoder) throws {
self.id = try decoder.value(forKey: "p_id")
self.name = try decoder.value(forKey: "p_name")
self.role = try decoder.value(forKey: "p_role")
self.imageURLString = try decoder.value(forKey: "general_image")
}
}
This is My FIRST VC:
import UIKit
class PersonListViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var personPages: [PagedResult<Person>] = [] {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadPersons()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first else {
return
}
collectionView.deselectItem(at: selectedIndexPath, animated: animated)
}
var service = ExampleWebService()
private func loadPersons(page: Int = 0, resultsPerPage: Int = 5) {
service.persons(page: page, resultsPerPage: resultsPerPage) { (personPage) in
guard !self.loadedPersonPageNumbers.contains(page) else { return }
self.personPages.append(personPage)
self.updateLastIndexPath(personPage)
}
}
private(set) var lastIndexPath: IndexPath?
private func updateLastIndexPath(_ personPage: PagedResult<Person>) {
if personPage.results.isEmpty {
lastIndexPath = nil
}
else {
lastIndexPath = calculateLastIndexPath()
}
}
private func calculateLastIndexPath() -> IndexPath? {
guard let lastPage = personPages.last else { return nil }
let section = lastPage.pageNumber
let row = lastPage.results.count - 1
return IndexPath(row: row, section: section)
}
fileprivate var loadedPersonPageNumbers: [Int] {
return personPages.map { $0.pageNumber }
}
func person(at indexPath: IndexPath) -> Person? {
guard indexPath.section < personPages.count else {
return nil
}
guard indexPath.row < personPages[indexPath.section].results.count else {
return nil
}
let page = personPages[indexPath.section]
return page.results[indexPath.row]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let personViewController = segue.destination as? PersonViewController,
let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first else {
return
}
personViewController.person = person(at: selectedIndexPath)
}
#IBAction func exitToPersonsView(segue: UIStoryboardSegue) {
}
}
extension PersonListViewController: UICollectionViewDelegate {
fileprivate var nextPageIndex: Int {
guard let lastPage = personPages.last else {
return 0
}
return lastPage.pageNumber.advanced(by: 1)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath == lastIndexPath {
loadPersons(page: nextPageIndex)
}
}
}
extension PersonListViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return personPages.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return personPages[section].results.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PersonListCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! PersonListCollectionViewCell
cell.person = person(at: indexPath)
return cell
}
}
extension PersonListViewController: UINavigationBarDelegate {
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
}
And this is My Second VC:
import Foundation
import UIKit
final class PersonViewController: UIViewController {
#IBOutlet weak var imagesCollectionVIew: UICollectionView!
#IBOutlet weak var personRole: UILabel!
#IBOutlet weak var customNavigationBar: UINavigationBar!
var personImagesByID: [PagedResult<Person>] = [] {
didSet {
DispatchQueue.main.async {
self.imagesCollectionVIew.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadPersonImagesByID()
}
var person: Person?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let person = person {
title = person.name
personRole.text = person.role
}
customNavigationBar.topItem?.title = title
}
var service = ExampleWebService()
private func loadPersonImagesByID(page: Int = 0, resultsperPge: Int = 5) {
service.persons(page: page, resultsPerPage: resultsperPge) { (personPage) in
guard !self.loadedPersonPageNumbers.contains(page) else {
return
}
self.personImagesByID.append(personPage)
self.updateLastIndexPath(personPage)
}
}
private(set) var lastIndexPath: IndexPath?
private func updateLastIndexPath(_ personPage: PagedResult<Person>) {
if personPage.results.isEmpty {
lastIndexPath = nil
}
else {
lastIndexPath = calculateLastIndexPath()
}
}
private func calculateLastIndexPath() -> IndexPath? {
guard let lastPage = personImagesByID.last else {
return nil
}
let section = lastPage.pageNumber
let item = lastPage.results.count - 1
return IndexPath(row: item, section: section)
}
fileprivate var loadedPersonPageNumbers: [Int] {
return personImagesByID.map { $0.pageNumber }
}
func person(at indexPath: IndexPath) -> Person? {
guard indexPath.section < personImagesByID.count else {
return nil
}
guard indexPath.item < personImagesByID[indexPath.section].results.count else {
return nil
}
let page = personImagesByID[indexPath.section]
return page.results[indexPath.item]
}
}
extension PersonViewController: UICollectionViewDelegate {
fileprivate var nextPageIndex: Int {
guard let lastPage = personImagesByID.last else {
return 0
}
return lastPage.pageNumber.advanced(by: 1)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath == lastIndexPath {
loadPersonImagesByID(page: nextPageIndex)
}
}
}
extension PersonViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.imagesCollectionVIew.frame.height - 17, height: self.imagesCollectionVIew.frame.height - 17)
}
}
extension PersonViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return personImagesByID.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return personImagesByID[section].results.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PersonViewCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImagesCollectionViewCell", for: indexPath) as! PersonViewCollectionViewCell
cell.person = person(at: indexPath)
return cell
}
}
extension PersonViewController: UINavigationBarDelegate {
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
}
Now my issue is that I am having a problem of how to write correctly my second struct PersonByID and when clicking on the person from First VC to show me data in Second VC from personByID URL Path.

UICollectionView Drag and Drop cell between collectionView

Hi I am using KDDragAndDropCollectionView for drag and drop feature between two three different collectionView. Everything is working fine, but I am not able to restrict the movement for particular case. I have three collectionView. The user can drop from A to B and B to C. But He cannot drah and drop from A to C or C to B or B to A.
Here is my code.
import SlideMenuControllerSwift
class MainViewController: NavigationBarViewController,KDDragAndDropCollectionViewDataSource {
#IBOutlet weak var inProgressView: UIView!
#IBOutlet weak var doneview: UIView!
#IBOutlet weak var toDoView: UIView!
#IBOutlet weak var doneCollectionView: UICollectionView!
#IBOutlet weak var inProgressCollectionView: UICollectionView!
#IBOutlet weak var toDoCollectionView: UICollectionView!
var drop: UIDropDown!
var toDoDataArray = [String]()
var inProgressDataArray = [String]()
var doneDataArray = [String]()
var dragAndDropManager : KDDragAndDropManager?
//MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
toDoDataArray.append("A")
toDoDataArray.append("B")
toDoDataArray.append("C")
inProgressDataArray.append("D")
doneDataArray.append("B")
doneDataArray.append("C")
// Do any additional setup after loading the view.
self.setUp()
self.setupDropDown()
self.dragAndDropManager = KDDragAndDropManager(canvas: self.view, collectionViews: [toDoCollectionView, inProgressCollectionView,doneCollectionView])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
}
//MARK: - Private Method
func setupDropDown() {
drop = UIDropDown(frame: CGRect(x: 24, y: 80, width: 200, height: 40))
//drop.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY)
drop.placeholder = "Select Month"
drop.options = ["Weekly", "Monthly", "Bi-Annual", "Annual"]
drop.didSelect { (option, index) in
self.drop.placeholder = option
print("You just select: \(option) at index: \(index)")
}
self.view.addSubview(drop)
}
func setUp()
{
//Setup Navigation Bar
self.menuIconImage = #imageLiteral(resourceName: "hamIco")
self.setNavigationBarButtonItem()
self.setNavigationBarItem()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
//MARK: - Navigation Bar Button Action Method
//MARK: - Button Action Methods
/**
Button Action method. Gets called when the left navigation item
Parameters: sender - the button on which the event occurred
**/
#IBAction func leftBtnAction(sender: UIButton) {
self.toggleLeft()
}
// MARK: - UITableView DataSource abd Delegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("Step 1 called")
if collectionView.tag == 0 {
return toDoDataArray.count
}
else if collectionView.tag == 1 {
return inProgressDataArray.count
}
return doneDataArray.count
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("Step 2 called")
if collectionView.tag == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TO_DO_COLLECTION_CELL_ID, for: indexPath) as! ToDoCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else if collectionView.tag == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IN_PROGRESS_COLLECTION_CELL_ID, for: indexPath) as! InProgressCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DONE_COLLECTION_Cell_ID, for: indexPath) as! DoneCollectionViewCell
cell.isHidden = false
if let kdCollectionView = collectionView as? KDDragAndDropCollectionView {
if let draggingPathOfCellBeingDragged = kdCollectionView.draggingPathOfCellBeingDragged {
if draggingPathOfCellBeingDragged.item == indexPath.item {
cell.isHidden = true
}
}
}
return cell
}
}
// MARK : KDDragAndDropCollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, dataItemForIndexPath indexPath: IndexPath) -> AnyObject {
print("Step 3 called")
if collectionView.tag == 0 {
return toDoDataArray[indexPath.item] as AnyObject
}
else if collectionView.tag == 1 {
return inProgressDataArray[indexPath.item] as AnyObject
}
return doneDataArray[indexPath.item] as AnyObject
//return data[collectionView.tag][indexPath.item]
}
func collectionView(_ collectionView: UICollectionView, insertDataItem dataItem : AnyObject, atIndexPath indexPath: IndexPath) -> Void {
print("Step 4 called")
if collectionView.tag == 0 {
if let di = dataItem as? String {
toDoDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else if collectionView.tag == 1 {
if let di = dataItem as? String {
inProgressDataArray.insert(di, at: indexPath.item)
//data[collectionView.tag].insert(di, at: indexPath.item)
}
}
else{
if let di = dataItem as? String {
doneDataArray.insert(di, at: indexPath.item)
}
}
}
func collectionView(_ collectionView: UICollectionView, deleteDataItemAtIndexPath indexPath : IndexPath) -> Void {
print("Step 5 called")
if collectionView.tag == 0 {
toDoDataArray.remove(at: indexPath.item)
}
else if collectionView.tag == 1 {
inProgressDataArray.remove(at: indexPath.item)
}
else{
doneDataArray.remove(at: indexPath.item)
}
}
func collectionView(_ collectionView: UICollectionView, moveDataItemFromIndexPath from: IndexPath, toIndexPath to : IndexPath) -> Void {
print("Step 6 called")
if collectionView.tag == 0 {
let fromDataItem: String = toDoDataArray[from.item]
toDoDataArray.remove(at: from.item)
toDoDataArray.insert(fromDataItem, at: to.item)
}
else if collectionView.tag == 1 {
let fromDataItem: String = inProgressDataArray[from.item]
inProgressDataArray.remove(at: from.item)
inProgressDataArray.insert(fromDataItem, at: to.item)
}
else{
let fromDataItem: String = doneDataArray[from.item]
doneDataArray.remove(at: from.item)
doneDataArray.insert(fromDataItem, at: to.item)
}
}
func collectionView(_ collectionView: UICollectionView, indexPathForDataItem dataItem: AnyObject) -> IndexPath? {
print("Step 7 called")
if collectionView.tag == 0 {
if let candidate : String = dataItem as? String {
for item : String in toDoDataArray {
if candidate == item {
let position = toDoDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else if collectionView.tag == 1 {
if let candidate : String = dataItem as? String {
for item : String in inProgressDataArray {
if candidate == item {
let position = inProgressDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
else{
if let candidate : String = dataItem as? String {
for item : String in doneDataArray {
if candidate == item {
let position = doneDataArray.index(of: item)! // ! if we are inside the condition we are guaranteed a position
let indexPath = IndexPath(item: position, section: 0)
return indexPath
}
}
}
}
return nil
}
}
//MARK: - Extension written for Slide the Menu
extension MainViewController : SlideMenuControllerDelegate {
func leftWillOpen() {
print("SlideMenuControllerDelegate: leftWillOpen")
}
func leftDidOpen() {
print("SlideMenuControllerDelegate: leftDidOpen")
}
func leftWillClose() {
print("SlideMenuControllerDelegate: leftWillClose")
}
func leftDidClose() {
print("SlideMenuControllerDelegate: leftDidClose")
}
func rightWillOpen() {
print("SlideMenuControllerDelegate: rightWillOpen")
}
func rightDidOpen() {
print("SlideMenuControllerDelegate: rightDidOpen")
}
func rightWillClose() {
print("SlideMenuControllerDelegate: rightWillClose")
}
func rightDidClose() {
print("SlideMenuControllerDelegate: rightDidClose")
}
}

ViewDid Load of today Widget gets called every time

I have implemented today widget for displaying recently recorded videos thumb.i am using collection view in Widget Storyboard file.
Its all working fine. I am able to get required thumbs from shared container and bind it to collection view as well. Now the issue is Whenever i look for the widget in Notification centre it calls ViewDidLoad of TodayWidgetViewController every time. Because of this collection view seems to be reloaded every time.
Is there a way to prevent this being reloaded every time?
Thanks in advance.
Below is the Source Code Implemented:
{
#IBOutlet weak var widgetCollection: UICollectionView!
#IBOutlet weak var recordButton: UIButton!
var mutArryListOfVideosImg: NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
self.recordButton.layer.borderWidth = 2.0
self.recordButton.layer.borderColor = UIColor.white.cgColor
self.recordButton.layer.masksToBounds = true
self.recordButton.layer.cornerRadius = self.recordButton.frame.size.width / 2
self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded
//print("array count:\()")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.getVideoThumbImages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnRecordAction(sender:UIButton)
{
let pjUrl = URL(string: "")
self.extensionContext?.open(pjUrl!, completionHandler: { (Bool) in
})
}
func getDataPath() -> URL
{
let appGroupId = “”
let appGroupDirectoryPath:URL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupId)!
let newDirectory = appGroupDirectoryPath.appendingPathComponent("VideoThumbs")
return newDirectory
}
func getVideoThumbImages()
{
let videoDirectoryPath = self.getDataPath()
if let Images = try? FileManager.default.contentsOfDirectory(atPath: videoDirectoryPath.path) as [String]
{
if((Images.count) > 0)
{
let sortedImages = (Images.sorted(by: backward)) as [String]
//print("Sorted Array:\(sortedImages)")
if((sortedImages.count) > 0)
{
for (index,element) in (sortedImages.enumerated()) {
print("\(index) = \(element)")
mutArryListOfVideosImg.add(element)
}
if(mutArryListOfVideosImg.count > 0)
{
widgetCollection.reloadData()
}
}
}
}
}
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
func getDataFromUrl(url: URL, completion: #escaping (_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void) {
URLSession.shared.dataTask(with: url) {
(data, response, error) in
completion(data, response, error)
}.resume()
}
//MARK: CollectionView Delegate and Datasource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
if mutArryListOfVideosImg.count > 3
{
return 3
}
else
{
return mutArryListOfVideosImg.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WidgetCollectionCell", for: indexPath as IndexPath) as! WidgetCollectionCell
cell.imgVideoThumbImage.layer.cornerRadius = 8.0
cell.imgVideoThumbImage.layer.masksToBounds = true
let directoryPath = self.getDataPath()
let url = directoryPath.appendingPathComponent(mutArryListOfVideosImg.object(at: indexPath.item) as! String)
getDataFromUrl(url: url) { (data, response, error) in
guard let data = data, error == nil else { return }
//print(response?.suggestedFilename ?? url.lastPathComponent)
//print("Download Finished")
DispatchQueue.main.async() { () -> Void in
cell.imgVideoThumbImage.image = UIImage(data: data)
}
}
return cell
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
print("Widget thumb tapped....")
let pjUrl = URL(string: “CallBack://edit-\(mutArryListOfVideosImg.object(at: indexPath.item))")
self.extensionContext?.open(pjUrl!, completionHandler: { (Bool) in
})
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: (self.widgetCollection.frame.size.width/3) - 10, height: 70)
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.newData)
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
if (activeDisplayMode == NCWidgetDisplayMode.compact) {
self.preferredContentSize = maxSize
}
else {
self.preferredContentSize = CGSize(width: maxSize.width, height: 120)
}
}
}
viewWillAppear and viewDidAppear will be called every time your widget is displayed.
About viewDidLoad seems strange, are you sure about it? Anyway this should not happen, it's not normal, try to check this method:
func widgetPerformUpdate(completionHandler: ((NCUpdateResult) -> Void)) {
// you could call here a custom function to update your widget
completionHandler(NCUpdateResult.NewData)
}
Your extension probably has an error and everytime view appears it is being called again.
Hope it helps.
Update: (after your corrections)
Seems your widget crash or not loaded due to size problems.
In viewWillAppear try to launch this method:
func setPreferredContentSize() {
var currentSize: CGSize = self.preferredContentSize
currentSize.height = 190.0
self.preferredContentSize = currentSize
}

Resources