How to increase size of UICollectionView on Scroll - swift

I want to increase the size of collectionView on Scroll. It's called Parallax affect.
I have 3 classes with named
1)class PhotoGalleryViewController' with storyboard.
PhotoGalleryViewController contains collectionView and a cell with imageview.
2) class ListingDetailContentViewController' without storyboard
3) class ListingDetailViewController' with storyboard.
ListingDetailViewController is appending ListingDetailContentViewController as child.
contentViewController = ListingDetailContentViewController(hideComparable: comparableIsHidden)
contentViewController.view.translatesAutoresizingMaskIntoConstraints = false
contentViewController.listingDetailViewController = self
addChild(contentViewController)
view.addSubview(contentViewController.view)
view.sendSubviewToBack(contentViewController.view)
ListingDetailContentViewController is appending photoGalleryViewController using Library AloeStackViewController. AloeStackViewController is UIScrollView so thats why i made its delegate in ListingDetailContentViewController
class ListingDetailContentViewController: AloeStackViewController, PhotoGalleryViewControllerDelegate, UIScrollViewDelegate {
// MARK: - Private functions
private func setupView(ad: Ad) {
// Display photos
if let photos = ad.photos {
photoGalleryViewController = UIStoryboard(name: "Listing", bundle: nil).instantiateViewController(withIdentifier: "PhotoGalleryViewController") as? PhotoGalleryViewController
let photoView = photoGalleryViewController.view!
stackView.addRow(photoView)
stackView.setInset(forRow: photoView, inset: .zero)
photoGalleryViewController.photos = photos
photoGalleryViewController.ad = ad
photoGalleryViewController.delegate = self
actualFrame = photoGalleryViewController.collectionView.frame.height
}
// Some other stuff
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// The link i applied here is https://stackoverflow.com/questions/33481928/imageview-scaling-when-scrolling-down
let offsetY = scrollView.contentOffset.y
if offsetY < 0 {
photoGalleryViewController.collectionView.frame.size.height += actualFrame - offsetY
photoGalleryViewController.view.frame.size.height += actualFrame - offsetY
self.loadViewIfNeeded()
} else {
photoGalleryViewController.collectionView.frame.size.height = actualFrame
photoGalleryViewController.view.frame.size.height = actualFrame
self.loadViewIfNeeded()
}
}
}
I tried this help but not worked for me.
My image is at top of screen. and other things are below.
When ever user scrolls down. Image should be increase.
Kindly help me out to solve this problem.
Update:
Answer:
I have done it with help of library called ParallaxHeader. I simply add this library using Pod and follow the tutorial given in library.

After a lot of struggle I achieved my goal that were asked in Question.
I have done it with help of library called ParallaxHeader.
I simply add this library using Pod and follow the tutorial given in library.

Related

Add alpha to parentVC.view

I am trying to add alpha to the background view when tapped on a button. So far achieved adding blur but alpha not so much.
How can I add alpha to the background so that when the bottom sheet appears background will be darker and disabled.
let maxDimmedAlpha: CGFloat = 0.2
lazy var dimmedView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = maxDimmedAlpha
return view
}()
#objc func shareBtnClick() {
dimmedView.frame = self.parentVC.view.bounds
dimmedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.parentVC.view.addSubview(dimmedView)
if self.parentVC.navigationController != nil {
if self.parentVC.navigationController?.viewControllers.count == 1 {
showBottomSheet()
} else {
NotificationCenter.default.post(name: NSNotification.Name("ShowBottomSheet"), object: nil, userInfo: ["itemId": modalSheet(), "delegate": self])
}
} else {
showBottomSheet()
}
}
func showBottomSheet() {
let modalSheet = MainBottomSheet()
modalSheet.data = self.modalSheet()
modalSheet.delegate = self
modalSheet.modalPresentationStyle = .overCurrentContext
self.parentVC.present(modalSheet, animated: true)
}
I was able to produce the dimmed effect using this code in XCode, I'm not sure why it won't work in your project but there is an easy way to debug this.
I suggest using Debug View Hierarchy, one of XCode's best tools in my opinion. This allows you to separate every single layer of the user interface. This way, you can see if your dimmedView is actually being added to the parent view and that its frame is matching the parent view's bounds.
Keep in mind if your background is dark, you won't see this dimmedView because its backgroundColor is set to UIColor.black.
Debug View Hierarchy button

How can I implement this UICollectionView cell animation?

I've been wanting to implement this nice little UICollectionViewCell animation shown below that was on Dribble.
Do you think it's possible?
I looked around for any guides to get a head start on this but found nothing quite similar.
I have the idea that a custom flow layout is the way to go here. Would it be that I will have to make snapshots of each visible cell, add pan gestures to each cell and based on the movement detected through the gesture recogniser, capture visible cells and animate the snapshot images? Would appreciate any help to understand how I could implement this.
Thank you.
This is a pretty interesting challenge.
Instead of doing a custom layout, I would override scrollViewDidScroll, store the offset every time it's called, compare it with the last stored offset in order to get the velocity, and based off of that, apply a transform to all visibleCells in your collection view.
var lastOffsetX: CGFloat?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
defer { lastOffsetX = scrollView.contentOffset.x }
guard let lastOffsetX = lastOffsetX else { return }
// You'll have to evaluate how large velocity gets to avoid the cells
// from stretching too much
let maxVelocity: CGFloat = 60
let maxStretch: CGFloat = 10
let velocity = min(scrollView.contentOffset.x - lastOffsetX, maxVelocity)
let stretch = velocity / maxVelocity * maxStretch
var cumulativeStretch: CGFloat = 0
collectionView.visibleCells.forEach { cell in
cumulativeStretch += stretch
cell.transform = CGAffineTransform(translateX: cumulativeStretch, y: 0)
}
}
I would start with something like this, and make lastOffsetX = nil when the scroll view stops scrolling (this exercise is left to the reader).
It will probably require some tweaking.

Identifying Objects in Firebase PreBuilt UI in Swift

FirebaseUI has a nice pre-buit UI for Swift. I'm trying to position an image view above the login buttons on the bottom. In the example below, the imageView is the "Hackathon" logo. Any logo should be able to show in this, if it's called "logo", since this shows the image as aspectFit.
According to the Firebase docs page:
https://firebase.google.com/docs/auth/ios/firebaseui
You can customize the signin screen with this function:
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
return FUICustomAuthPickerViewController(nibName: "FUICustomAuthPickerViewController",
bundle: Bundle.main,
authUI: authUI)
}
Using this code & poking around with subviews in the debuggers, I've been able to identify and color code views in the image below. Unfortunately, I don't think that the "true" size of these subview frames is set until the view controller presents, so trying to access the frame size inside these functions won't give me dimensions that I can use for creating a new imageView to hold a log. Plus accessing the views with hard-coded index values like I've done below, seems like a pretty bad idea, esp. given that Google has already changed the Pre-Built UI once, adding a scroll view & breaking the code of anyone who set the pre-built UI's background color.
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
// Create an instance of the FirebaseAuth login view controller
let loginViewController = FUIAuthPickerViewController(authUI: authUI)
// Set background color to white
loginViewController.view.backgroundColor = UIColor.white
loginViewController.view.subviews[0].backgroundColor = UIColor.blue
loginViewController.view.subviews[0].subviews[0].backgroundColor = UIColor.red
loginViewController.view.subviews[0].subviews[0].tag = 999
return loginViewController
}
I did get this to work by adding a tag (999), then in the completion handler when presenting the loginViewController I hunt down tag 999 and call a function to add an imageView with a logo:
present(loginViewController, animated: true) {
if let foundView = loginViewController.view.viewWithTag(999) {
let height = foundView.frame.height
print("FOUND HEIGHT: \(height)")
self.addLogo(loginViewController: loginViewController, height: height)
}
}
func addLogo(loginViewController: UINavigationController, height: CGFloat) {
let logoFrame = CGRect(x: 0 + logoInsets, y: self.view.safeAreaInsets.top + logoInsets, width: loginViewController.view.frame.width - (logoInsets * 2), height: self.view.frame.height - height - (logoInsets * 2))
// Create the UIImageView using the frame created above & add the "logo" image
let logoImageView = UIImageView(frame: logoFrame)
logoImageView.image = UIImage(named: "logo")
logoImageView.contentMode = .scaleAspectFit // Set imageView to Aspect Fit
// loginViewController.view.addSubview(logoImageView) // Add ImageView to the login controller's main view
loginViewController.view.addSubview(logoImageView)
}
But again, this doesn't seem safe. Is there a "safe" way to deconstruct this UI to identify the size of this button box at the bottom of the view controller (this size will vary if there are multiple login methods supported, such as Facebook, Apple, E-mail)? If I can do that in a way that avoids the hard-coding approach, above, then I think I can reliably use the dimensions of this button box to determine how much space is left in the rest of the view controller when adding an appropriately sized ImageView. Thanks!
John
This should address the issue - allowing a logo to be reliably placed above the prebuilt UI login buttons buttons + avoiding hard-coding the index values or subview locations. It should also allow for properly setting background color (also complicated when Firebase added the scroll view + login button subview).
To use: Create a subclass of FUIAuthDelegate to hold a custom view controller for the prebuilt Firebase UI.
The code will show the logo at full screen behind the buttons if there isn't a scroll view or if the class's private constant fullScreenLogo is set to false.
If both of these conditions aren't meant, the logo will show inset taking into account the class's private logoInsets constant and the safeAreaInsets. The scrollView views are set to clear so that a background image can be set, as well via the private let backgroundColor.
Call it in any signIn function you might have, after setting authUI.providers. Call would be something like this:
let loginViewController = CustomLoginScreen(authUI: authUI!)
let loginNavigationController = UINavigationController(rootViewController: loginViewController)
loginNavigationController.modalPresentationStyle = .fullScreen
present(loginNavigationController, animated: true, completion: nil)
And here's one version of the subclass:
class CustomLoginScreen: FUIAuthPickerViewController {
private var fullScreenLogo = false // false if you want logo just above login buttons
private var viewContainsButton = false
private var buttonViewHeight: CGFloat = 0.0
private let logoInsets: CGFloat = 16
private let backgroundColor = UIColor.white
private var scrollView: UIScrollView?
private var viewContainingButton: UIView?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// set color of scrollView and Button view inside scrollView to clear in viewWillAppear to avoid a "color flash" when the pre-built login UI first appears
self.view.backgroundColor = UIColor.white
guard let foundScrollView = returnScrollView() else {
print("😡 Couldn't get a scrollView.")
return
}
scrollView = foundScrollView
scrollView!.backgroundColor = UIColor.clear
guard let foundViewContainingButton = returnButtonView() else {
print("😡 No views in the scrollView contain buttons.")
return
}
viewContainingButton = foundViewContainingButton
viewContainingButton!.backgroundColor = UIColor.clear
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Create the UIImageView at full screen, considering logoInsets + safeAreaInsets
let x = logoInsets
let y = view.safeAreaInsets.top + logoInsets
let width = view.frame.width - (logoInsets * 2)
let height = view.frame.height - (view.safeAreaInsets.top + view.safeAreaInsets.bottom + (logoInsets * 2))
var frame = CGRect(x: x, y: y, width: width, height: height)
let logoImageView = UIImageView(frame: frame)
logoImageView.image = UIImage(named: "logo")
logoImageView.contentMode = .scaleAspectFit // Set imageView to Aspect Fit
logoImageView.alpha = 0.0
// Only proceed with customizing the pre-built UI if you found a scrollView or you don't want a full-screen logo.
guard scrollView != nil && !fullScreenLogo else {
print("No scrollView found.")
UIView.animate(withDuration: 0.25, animations: {logoImageView.alpha = 1.0})
self.view.addSubview(logoImageView)
self.view.sendSubviewToBack(logoImageView) // otherwise logo is on top of buttons
return
}
// update the logoImageView's frame height to subtract the height of the subview containing buttons. This way the buttons won't be on top of the logoImageView
frame = CGRect(x: x, y: y, width: width, height: height - (viewContainingButton?.frame.height ?? 0.0))
logoImageView.frame = frame
self.view.addSubview(logoImageView)
UIView.animate(withDuration: 0.25, animations: {logoImageView.alpha = 1.0})
}
private func returnScrollView() -> UIScrollView? {
var scrollViewToReturn: UIScrollView?
if self.view.subviews.count > 0 {
for subview in self.view.subviews {
if subview is UIScrollView {
scrollViewToReturn = subview as? UIScrollView
}
}
}
return scrollViewToReturn
}
private func returnButtonView() -> UIView? {
var viewContainingButton: UIView?
for view in scrollView!.subviews {
viewHasButton(view)
if viewContainsButton {
viewContainingButton = view
break
}
}
return viewContainingButton
}
private func viewHasButton(_ view: UIView) {
if view is UIButton {
viewContainsButton = true
} else if view.subviews.count > 0 {
view.subviews.forEach({viewHasButton($0)})
}
}
}
Hope this helps any who have been frustrated trying to configure the Firebase pre-built UI in Swift.

Trying to hide SearchBar through Alpha in Swift

I'm using Xcode 9 and Swift 4. I'm having trouble trying to use the scroll in y to hide SearchBar through alpha. How to access this background to try to hide it?
code:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let transp:Float = Float(1 - (self.mTableView.contentOffset.y / (self.mTableView.contentSize.height - self.mTableView.frame.size.height) * 60))
self.navigationController!.navigationBar.alpha = CGFloat(transp)
self.mSearchController.searchBar.alpha = CGFloat(transp)
self.mSearchController.searchBar.isTranslucent = true
titleMsgView.alpha = CGFloat(transp)
}
Here's a sample gif link:
https://www.dropbox.com/s/6is5mukpk1yew8e/bgSearch.gif?dl=0
The SearchView is pretty tough when it comes to changing how it looks. You could try looping over the subviews of the SearchView and setting all their alpha values to 0, like this:
for subView in mSearchController.searchBar.subviews {
subView.alpha = 0
for deepSubView in subView.subviews {
deepSubView.alpha = 0
}
}
Also, a quick note: you can remove a lot of the self keywords from your code.
EDIT: That just removed the searchbar, but with some editing we can fix that:
for subview in mSearchController.searchBar.subviews.last!.subviews {
if type(of: subview) == NSClassFromString("UISearchBarBackground") {
subview.alpha = 0
}
}
That should do the trick!

How to get and change a variable in my view controller from SKScene

Ok so here is the code
class GameViewController: UIViewController, SceneTransitionDelegate,
GKGameCenterControllerDelegate, ADBannerViewDelegate {
var coolbool:Bool = false
...abunch of unimportant stuff functions and stuff
}
And here is what I am trying to do from my SKScene
func thing1()
{
let controller = GameViewController()
controller.coolbool = true
println(controller.coolbool) // Will say that it is true
sceneDelegate.transitionToScene(Menu.self) //Menu.self is the skscene that
we used to be in and will be in
}
func thing2()
{
println(controller.coolbool) // Will say that it is false
if (controller.coolbool == true)
{
//Put rainbows over every sprite and change generator settings
}
}
So basically what happens is that "coolbool" is initialized as being false. Until thing1() is called causing the variable "coolbool " to change. And i confirm its change immediately after, before the transition. However after the transition (to the same scene (I'm trying to make it look different if the bool is true)) if you ask what the value is, it says its false.... even though i just set it to true.
Anyway I assume I am doing something wrong, is their a better way to do this??? Incase you want it here is the transition function
func transitionToScene(sceneClass:Scene.Type) {
playing = false
var sizeRect = UIScreen.mainScreen().applicationFrame
var width = sizeRect.size.width * UIScreen.mainScreen().scale
var height = sizeRect.size.height * UIScreen.mainScreen().scale
let skView = self.view as! SKView
let scene = sceneClass(size: skView.bounds.size)
scene.size = CGSizeMake(width, height)
rwidth = width
rheight = height
swidth = width
sheight = height
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
scene.sceneDelegate = self
skView.presentScene(scene)
}
You need to get the current instance of the GameViewController, so this statement:
let controller = GameViewController()
isn't going to work because it declares a new instance of a controller, which is not related to the SKView you are currently using. Instead, we need to obtain the current, presenting view controller.
Obtaining Instance of the View Controller:
The closest method that can help us here is in the UIResponder class, and it is nextResponder:. Its general function is to find and return the current next responder, which seems vague and not of much use to us, until Apple mentions that:
Subclasses must override this method to set the next responder. UIView implements this method by returning the UIViewController object that manages it (if it has one) or its superview (if it doesn’t)
This means that if we are in a scene, we can find the view controller by using the scene's self.view which is the presenting SKView object (inherits from UIView). All we have to do is find the View Controller in our responder chain.
Swift:
var currentViewController: GameViewController?
var upstreamResponder: UIResponder? = self.view
var found = false
while (found != true){
upstreamResponder = upstreamResponder!.nextResponder()
if let viewController = upstreamResponder as? GameViewController {
currentViewController = viewController
found = true
}
if upstreamResponder == nil {
//could not find VC, PANIC!
break
}
}
Objective-C: (will probably have to #include "GameViewController.h")
GameViewController *currentViewController;
UIResponder *upstreamResponder = self.view;
BOOL found = false;
while (found != true) {
upstreamResponder = [upstreamResponder nextResponder];
if ([upstreamResponder isKindOfClass:[GameViewController class]]) {
currentViewController = (GameViewController *)upstreamResponder;
}
if (upstreamResponder == nil) {
//omg where did VC go?!
break;
}
}
Setting properties on your View Controller
Now that you have your current view controller, it is very simple to set any desired property. So if you wanted to change coolbool, all you have to do is state:
currentViewController.coolbool = true
Hope this helps! -Max