Can we get an array of all storyboard views - ios

I'm developping application in Swift.
This application has many view and I would like to put a UIProgressView on all views
Can we get an array of all storyboard views ?
for exemple :
self.progressBar = UIProgressView(progressViewStyle: .Bar)
self.progressBar?.center = view.center
self.progressBar?.frame = CGRect(x: 0, y: 20, width: view.frame.width, height: CGFloat(1))
self.progressBar?.progress = 1/2
self.progressBar?.trackTintColor = UIColor.lightGrayColor();
self.progressBar?.tintColor = UIColor.redColor();
var arrayViewController : [UIViewController] = [...,...,...]
for controller in arrayViewController {
controller.view.addSubview(self.progressBar)
}
Thank you
Ysée

I assume that what you really want is to have the progress displayed on every view IF there is an operation in progress.
There are many ways to do that (using delegation, NSNotificationCenter, …) but the easiest I can think of would be to rely on viewWillAppear
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Check if there's an operation in progress and add progressView if relevant
}
For the user, it will effectively look like you added the progress view to all views.

Why not create a base class that has a lazy stored property of type UIProgressView ? Optionally you can have two methods setProgressViewHidden(hidden : Bool) in order to easily show and hide the progress view and setProgress(progress : Float) to update the progress. Then all your view controllers can subclass this base class and conveniently interact with the progress view.
class ProgressViewController : UIViewController {
lazy var progressView : UIProgressView = {
[unowned self] in
var view = UIProgressView(frame: CGRectMake(0, 20, self.view.frame.size.width, 3))
view.progress = 0.5
view.trackTintColor = UIColor.lightGrayColor()
view.tintColor = UIColor.redColor()
self.view.addSubview(view)
return view
}()
}
To read more about lazy stored properties, check: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

Related

Putting loading animation over VNDocumentViewController Swift

Is it possible to put a loading animation over the VNDocumentViewController? As in, when the user presses the Save button, is there a way for me to somehow indicate that the Vision is processing the image and hasn't frozen? Right now, in my app, there is a long pause between the user pressing Save and the actual image being processed.Here is an example from another post of what I'm trying to create
Here is one example of adding a loading indicator using UIActivityIndicatorView().
startAnimating() to start the animation and stopAnimation() to stop the animation.
iOS - Display a progress indicator at the center of the screen rather than the view
guard let topWindow = UIApplication.shared.windows.last else {return}
let overlayView = UIView(frame: topWindow.bounds)
overlayView.backgroundColor = UIColor.clear
topWindow.addSubview(overlayView)
let hudView = UIActivityIndicatorView()
hudView.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
overlayView.addSubview(hudView)
hudView.center = overlayView.center
hudView.startAnimating()
Alternatively, you could look into using Cocoapod MBProgressHud
https://cocoapods.org/pods/MBProgressHUD
There's a way you can extend a class in Swift that captures this problem well. The idea is you want a UIActivityIndicator in your VNDocumentCameraViewController. But we'd like that to be a part of every version of this we use. We could simply embed the DocumentVC's view into our current view and superimpose a UIActivityIndicator above it in the view stack, but that's pretty hacky. Here's a quick way we can extend any class and solve this problem
import VisionKit
import UIKit
extension VNDocumentCameraViewController {
private struct LoadingContainer {
static var loadingIndicator = UIActivityIndicatorView()
}
var loadingIndicator: UIActivityIndicatorView {
return LoadingContainer.loadingIndicator
}
func animateLoadingIndicator() {
if loadingIndicator.superview == nil {
view.addSubview(loadingIndicator)
//Setup your constraints through your favorite method
//This constrains it to the very center of the controller
loadingIndicator.frame = CGRect(
x: view.frame.width / 2.0,
y: view.frame.height / 2.0,
width: 20,
height: 20)
//Setup additional state like color/etc here
loadingIndicator.color = .white
}
loadingIndicator.startAnimating()
}
func stopAnimatingLoadingIndicator() {
loadingIndicator.stopAnimating()
}
}
The place we can call these functions are in the delegate methods for VNDocumentCameraViewController that you implement in your presenting ViewController:
func documentCameraViewController(
_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan
) {
controller.animateLoadingIndicator()
}

how to custom flip and transition to another view controller with button click ? (n swift)

basically my current setup is like this
one storyboard ViewController with 3 types of UI View(container, front view, back view) inside of it.
what i want to accomplish (and i don't know how to implement #2)
user enters the data on the form(front of the card- View Controller number 1)
clicks the save button (do animation flipping and redirect to a new view controller)
the new view controller loads up (back of the card - View Controller number 2)
this is the current code flip example:
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var goButton: UIButton!
#IBOutlet weak var optionsSegment: UISegmentedControl!
let owlImageView = UIImageView(image: UIImage(named:"img-owl"))
let catImageView = UIImageView(image: UIImage(named:"img-cat"))
var isReverseNeeded = false
override func viewDidLoad() {
super.viewDidLoad()
title = "Transitions Test"
setupView()
}
fileprivate func setupView() {
let screen = UIScreen.main.bounds
goButton.layer.cornerRadius = 22
//container to hold the two UI views
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 250))
containerView.backgroundColor = UIColor(red: 6/255, green: 111/255, blue: 165/255, alpha: 1.0)
containerView.layer.borderColor = UIColor.white.cgColor
containerView.layer.borderWidth = 2
containerView.layer.cornerRadius = 20
containerView.center = CGPoint(x: screen.midX, y: screen.midY)
view.addSubview(containerView)
//front view
catImageView.frame.size = CGSize(width: 100, height: 100)
catImageView.center = CGPoint(x: containerView.frame.width/2, y: containerView.frame.height/2)
catImageView.layer.cornerRadius = 50
catImageView.clipsToBounds = true
//back view
owlImageView.frame.size = CGSize(width: 100, height: 100)
owlImageView.center = CGPoint(x: containerView.frame.width/2, y: containerView.frame.height/2)
owlImageView.layer.cornerRadius = 50
owlImageView.clipsToBounds = true
containerView.addSubview(owlImageView)
}
#IBAction func goButtonClickHandler(_ sender: Any) {
doTransition()
}
fileprivate func doTransition() {
let duration = 0.5
var option:UIViewAnimationOptions = .transitionCrossDissolve
switch optionsSegment.selectedSegmentIndex {
case 0: option = .transitionFlipFromLeft
case 1: option = .transitionFlipFromRight
case 2: option = .transitionCurlUp
case 3: option = .transitionCurlDown
case 4: option = .transitionCrossDissolve
case 5: option = .transitionFlipFromTop
case 6: option = .transitionFlipFromBottom
default:break
}
if isReverseNeeded {
UIView.transition(from: catImageView, to: owlImageView, duration: duration, options: option, completion: nil)
} else {
UIView.transition(from: owlImageView, to: catImageView, duration: duration, options: option, completion: nil)
}
isReverseNeeded = !isReverseNeeded
}
}
There are a few alternatives for transition between view controllers with a flipping animation:
You can define a segue in IB, configure that segue to do a horizontal flipping animation:
If you want to invoke that segue programmatically, give the segue a “Identifier” string in the attributes inspector and then you can perform it like so:
performSegue(withIdentifier: "SecondViewController", sender: self)
Alternatively, give the actual destination view controller’s scene a storyboard identifier, and the presenting view controller can just present the second view controller:
guard let vc = storyboard?.instantiateViewController(identifier: "SecondViewController") else { return }
vc.modalTransitionStyle = .flipHorizontal
vc.modalPresentationStyle = .currentContext
show(vc, sender: self)
If this standard flipping animation isn’t quite what you want, you can customize it to your heart’s content. iOS gives us rich control over custom transitions between view controller by specifying transitioning delegate, supplying an animation controller, etc. It’s a little complicated, but it’s outlined in WWDC 2017 Advances in UIKit Animations and Transitions: Custom View Controller Transitions (about 23:06 into the video) and WWDC 2013 Custom Transitions Using View Controllers.

"removeFromSuperview" NEVER work on UIApplication?

How to remove subviews?
I am trying to integrate GIF by creating UIView and UIImageView programmatically.
It works fine to show GIF but when the function of hiding if is called, there is no response.
Here are the codes of both functions.
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
lazy var transparentView: UIView = {
let transparentView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
transparentView.backgroundColor = viewColor.withAlphaComponent(setAlpha)
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.center = transparentView.center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
self.addSubview(self.transparentView)
self.transparentView.addSubview(self.gifImage)
self.transparentView.bringSubview(toFront: self.gifImage)
UIApplication.shared.keyWindow?.addSubview(transparentView)
}
func hideLoaderView() {
self.transparentView.removeFromSuperview()
}
}
A couple of thoughts:
I’d suggest you add a breakpoint or a logging statement in hideLoaderView and make sure you’re getting to that line.
You should make the init method to this class private to make sure you’re not calling hideLoaderView on some separate instance. When dealing with singletons, you want to make sure you can’t accidentally create another instance.
But I tested your code, and it works fine. Your problem probably rests with where and how you call this (and making init private, you might find where you might be using it inappropriately).
In the comments below, you said:
I simply call the function "CustomLoader().hideLoaderView()" Both are being called technically. What do you mean by "where I using it inappropriately?"
That is the root of the problem.
The CustomLoader() of CustomLoader().hideLoaderView() will create a new instance of CustomLoader with its own transparencyView, etc., which is precisely what the problem is. You’re not hiding the old view that was presented earlier, but trying to hide another one that you just created and was never displayed.
If you instead use that static, e.g. CustomLoader.instance.showLoaderView() and CustomLoader.instance.hideLoaderView(), then the problem will go away. Then you will be hiding the same view that your previously showed.
By the way, a few other unrelated observations:
If this is a singleton or shared instance, the convention would be to call that static property shared, not instance.
By the way, you aren’t using this CustomLoader as a UIView, so I’d not make it a UIView subclass. Don’t make it a subclass of anything.
You would obviously eliminate that self.addSubview(transparentView) line, too.
The bringSubview(toFront:) call is unnecessary.
You should avoid referencing UIScreen.main.bounds. You don’t know if your app might be in multitasking mode (maybe this isn’t an issue right now, but it’s the sort of unnecessary assumption that will cause problems at some later date). Just refer to the bounds of the UIWindow to which you’re adding this. You should also update this frame when you show this view, not when you create it (in case you changed orientation in the intervening time, or whatever).
By the way, using keyWindow is discouraged in iOS 13 and later, so you might eventually want to remove that, too.
When adding the gifImage (which I’d suggest renaming to gifImageView because it’s an image view, not an image), you should not reference the center of its superview. That’s the coordinate of the transparent view in its super view’s coordinate system, which could be completely different than the transparent view’s own coordinate system. In this case, it just happens to work, but it suggests a fundamental misunderstanding of view coordinate systems. Reference the bounds of the transparentView, not its center.
If you’re going to expose viewColor and setAlpha, you should pull the setting of the transparentView’s color out of the lazy initializer and into showLoaderView, at the very least. Right now, if you show the loader once, and then change the color, and try to show it again, you won’t see the new color.
The same issue applies with the gif image. So, I’d move that to the didSet observer.
Thus, pulling this all together:
class CustomLoader{
static let shared = CustomLoader()
private init() { }
var dimmingColor: UIColor = .black
var dimmingAlpha: CGFloat = 0.5
var gifName: String = "" { didSet { gifImage.loadGif(name: gifName) } }
lazy var transparentView: UIView = {
let transparentView = UIView()
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImageView: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.isUserInteractionEnabled = false
return gifImage
}()
func showLoaderView() {
guard let window = UIApplication.shared.keyWindow else { return }
transparentView.frame = window.bounds
transparentView.backgroundColor = dimmingColor.withAlphaComponent(dimmingAlpha)
gifImageView.center = CGPoint(x: transparentView.bounds.midX, y: transparentView.bounds.midY)
transparentView.addSubview(gifImageView)
window.addSubview(transparentView)
}
func hideLoaderView() {
transparentView.removeFromSuperview()
}
}
Why you are using transparentView while you are have a CustomLoader instance view
Try to use this
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
init() {
super.init(frame: UIScreen.main.bounds)
backgroundColor = viewColor.withAlphaComponent(setAlpha)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.backgroundColor = .red
gifImage.contentMode = .scaleAspectFit
gifImage.center = center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
addSubview(self.gifImage)
UIApplication.shared.keyWindow?.addSubview(self)
}
func hideLoaderView() {
removeFromSuperview()
}
}

Memory stays high after deallocating UINavigationController

I got UINavigationController inside my app with a rootVC "VC1", VC1 containts a collectionview with 2images inside every cell. When user select cell, navigationcontroller will pass images from cell to new vc "VC2" and then push it to the top of navigationcontroller. My problem is when I take down VC2 via popviewcontroller, VC2 is deallocated correctly but memory stays at the same higher level (after pushing new vc it increases from 60mb to 130mb). I've trying set image to nil, and imageview also but none of this work. Here's some of my code:
class VC1: UIViewController {
var selectedUserPollDetails : VC2?
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! AppUserCell
selectedUserPollDetails = VC2()
selectedUserPollDetails?.leftPhoto = cell.leftImageNode.image
selectedUserPollDetails?.rightPhoto = cell.rightImageNode.image
navigationController?.pushViewController(selectedUserPollDetails !, animated: true)
}
}
class VC2: UIViewController {
lazy var arrow : ArrowBack = {
let arrow = ArrowBack()
return arrow
}()
weak var leftPhoto: UIImage?
weak var rightPhoto: UIImage?
var leftPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFill
imageview.layer.cornerRadius = 5
imageview.layer.masksToBounds = true
return imageview
}()
var rightPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFit
imageview.layer.cornerRadius = 5
imageview.clipsToBounds = true
return imageview
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(leftPhotoImageview)
view.addSubview(rightPhotoImageview)
view.addSubview(arrow)
arrow.addTarget(self, action: #selector(handleArrowBack), for: .touchUpInside)
}
func handleArrowBack(){
navigationController?.popViewController(animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
leftPhotoImageview.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
rightPhotoImageview.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
if leftPhoto != nil, rightPhoto != nil{
leftPhotoImageview.image = leftPhoto
rightPhotoImageview.image = rightPhoto
}
}
deinit{
leftPhoto = nil
rightPhoto = nil
leftPhotoImageview.image = nil
rightPhotoImageview.image = nil
}
I've even added deinit at the end to make sure photos are deallocated. So basically when i'm trying to push VC2 again (after pop) amount of memory is doubled again (260mb) and so on... what causing this problem? what am i doing wrong?
btw. i've omitted less important func and vars
You defo have a memory leak. I believe everytime
you push a new view via the nav controller it creates a brand new view i.e. the view is completely new and isn't reused. If you have strong references within the view you've pushed to they won't be released unless you deinit as they have a strong ref to view you've pushed so they linger around. You mentioned you deinit those items. Have you also tried marking leftPhotoImageView and rightPhotoImageView also as weak properties? Something is holding onto those images it would seem.
You could also change deinit to leftPhotoImageview = nil and rightPhotoImageView = nil rather than setting the imageview.image property to nil if that makes sense.
Ok, i think i found a solution, i don't know why i haven't trying this one, so i marked my imageview as lazy, and now memory is decreasing after popping vc

Create UIContainerView programmatically

I've just started to code my app in Swift 2 and avoiding the use of XIBs and storyboards.
However, I am unable to replicate the following feature. It's exactly what I wanted.
I've tried creating a UIView to perform the following using .backgroundColor and it works, however, I am unable to link it to my UIViewControllers. Just wondering how is it done? How do I link my UIView to my UIViewController?
Codes:
let subFrame : CGRect = CGRectMake(0,screenHeight*1/2.75,screenWidth,screenHeight)
var loginView = SignUpViewController()
let signUpView: UIView = UIView(frame: subFrame)
signUpView.backgroundColor = UIColor.redColor()
//Controls what each segment does
switch segmentView.indexOfSelectedSegment {
case 0:
self.view.addSubview(signUpView)
case 1:
self.view.addSubview(loginView)
default:
break;
}
I'm not even sure if .view.addSubview(xxx) overwrites/replaces the original subview if it is not this way. Is this the right way to do it?
Do not just start coding an app if you are not familiar with simple things of the OOP (Object-Oriented-Programming) language like Swift. This is not the way how to learn a programming language. Sure you could learn while experimenting but it is better to understand the book first before starting with more complex stuff. Read a few more pages of the Swift book from Apple. Most classes for iOS development are still Objective-C wrapped classes (reference type because the top superClass is probably NSObject; keep this in mind).
Here is the code example you wanted:
class ViewController: UIViewController {
let firstView = UIView()
let secondView = UIView()
let segmentedControlView = UISegmentedControl(items: ["firstView", "secondView"])
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor() // we need this for the playground
/* setup your view here */
/* add your sigment logic somewhere */
self.view.addSubview(self.segmentedControlView)
self.view.addSubview(self.firstView)
self.view.addSubview(self.secondView)
self.segmentedControlView.frame = CGRect(x: 0, y: 20, width: self.view.frame.width, height: 44)
self.segmentedControlView.selectedSegmentIndex = 0 // enable the first segment
self.segmentedControlView.addTarget(self, action: "segmentIndexChanged:", forControlEvents: UIControlEvents.ValueChanged)
/* add your own frame calculation here */
/* I prefer AutoLayout, but for the example static frames will be fine */
self.firstView.frame.origin = CGPoint(x: 0, y: self.segmentedControlView.frame.origin.y + self.segmentedControlView.frame.height)
self.firstView.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.height - self.segmentedControlView.frame.origin.y)
// to prevent same code, we just copy the same frame from the firstView
// both will sit in the same place
self.secondView.frame = self.firstView.frame
/* lets add some colors so we'll see our views */
self.firstView.backgroundColor = UIColor.blueColor()
self.secondView.backgroundColor = UIColor.redColor()
self.secondView.hidden = true // when intializer the secondView is not visible
}
func segmentIndexChanged(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
self.firstView.hidden = false
self.secondView.hidden = true
case 1:
self.firstView.hidden = true
self.secondView.hidden = false
default:
break;
}
}
}
If you do not understand a function, should should look up its definition in the developer docs. (Like: addSubview)

Resources