UIKit Change Presented View Controller's Layout Constraints Relative to Parent View - ios

I'm working on a project in UIKit, without storyboards (only programmatic layout constraints) and, following this, I have a custom view controller like this:
#objc public class testController: UIViewController, QLPreviewControllerDataSource {
public override func viewDidAppear(_ animated: Bool) {
let previewController = QLPreviewController()
previewController.dataSource = self
self.view.translatesAutoresizingMaskIntoConstraints = false
previewController.view.widthAnchor.constraint(equalToConstant: 200).isActive = true
present(previewController, animated: true, completion: nil)
}
public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
guard let url = Bundle.main.url(forResource: String("beans"), withExtension: "pdf") else {
fatalError("Could not load \(index).pdf")
}
return url as QLPreviewItem
}
}
Then, in my main View Controller file, I add this testController as a subview like so:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let test = testController()
self.view.addSubview(test.view)
test.view.translatesAutoresizingMaskIntoConstraints = false
}
}
This works fine, but I'd like to be able to change my testController's programmatic layout constraints relative to it's parent view.
I've tried stuff like this in the main view controller (ViewController):
let test = testController()
self.view.addSubview(test.view)
test.view.translatesAutoresizingMaskIntoConstraints = false
test.view.widthAnchor.constraint(equalTo: 200, constant: 0).isActive = true
but this simply doesn't work/the view doesn't reflect these constraints at all and it seems like the only way I can successfully modify the constraints of the testController, is within the viewDidAppear function of the testController class.
However, if I try something like this:
public override func viewDidAppear(_ animated: Bool) {
let previewController = QLPreviewController()
previewController.dataSource = self
self.view.translatesAutoresizingMaskIntoConstraints = false
previewController.view.widthAnchor.constraint(equalToConstant: 200).isActive = true //notice how this works since it's a hardcoded 200
previewController.view.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0).isActive = true //this throws an error
present(previewController, animated: true, completion: nil)
}
I get an error thrown.
So I'd somehow like to access the parent of testViewController I guess, and use it for the constraints of the view. I've tried unsuccessfully using presentingViewController and parent for this, but they either return nil or throw an error.
Any help here would be appreciated.

This is sample to add view and change the constraints, in your example you have to add more constraint to test view.
class ViewController: UIViewController {
let buttonTest: UIButton = {
let button = UIButton()
button.setTitle("go to ", for: .normal)
button.backgroundColor = .green
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addSubview(buttonTest)
buttonTest.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
buttonTest.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
buttonTest.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
])
}
#objc func buttonPressed() {
let secondView = SecondViewController()
self.view.addSubview(secondView.view)
secondView.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
secondView.view.topAnchor.constraint(equalTo: self.view.topAnchor,constant: 100),
secondView.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
secondView.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
secondView.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -100)
])
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
}
}

Related

How to change Status Bar Style inside ViewContoller if app based on SwiftUI

I'll try to change the status bar style inside the view controller because I want to use a different style depending on ViewController, and I try each of these recommendations How to change Status Bar text color in iOS, and I change option inside Info.plist , also not have an effect, what is wrong?
My code:
import SwiftUI
import UIKit
#main
struct TestAppearanceApp: App {
var body: some Scene {
WindowGroup {
WrapperUIVC_Hello().edgesIgnoringSafeArea(.all)
}
}
}
struct WrapperUIVC_Hello: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<WrapperUIVC_Hello>) -> some UIViewController {
let controller = ViewController()
controller.modalPresentationStyle = .automatic
return controller
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: UIViewControllerRepresentableContext<WrapperUIVC_Hello>) {
}
}
class ViewController: UIViewController {
lazy var button: UIButton = {
let view = UIButton()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .brown
view.addTarget(self, action: #selector(addInteraction(_:)), for: .touchUpInside)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
view.addSubview(button)
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc func addInteraction(_ sender: UIButton) {
let vc = ViewControllerTest()
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidAppear(_ animated: Bool) {
setNeedsStatusBarAppearanceUpdate()
}
}
class ViewControllerTest: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
setNeedsStatusBarAppearanceUpdate()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.dismiss(animated: true)
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override func viewDidAppear(_ animated: Bool) {
setNeedsStatusBarAppearanceUpdate()
}
}

Swift Delegate Pattern: Delegate = Nil

I know that this question has been asked a lot here and I've read all of them, but my problem is still persistent. I wanted to include the delegate pattern in my project in order to call a function in another class from one class. But it didn't work there either. So I created a completely new project to practice the pattern again. Only for 3 hours I just can't do it. My delegate still stays in my main ViewController nil.
This is my Code of my ViewController. It is the initialised ViewController and also the master of the delegate pattern, and the buttonViewController is the servant:
import UIKit
protocol ViewControllerDelegate {
func printTest(message: String)
}
class ViewController: UIViewController {
var delegate: ViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func perform(_ sender: UIButton) {
let vc = storyboard?.instantiateViewController(withIdentifier: "vc") as! ButtonViewController
if delegate == nil {
print("Ist nil")
}
delegate?.printTest(message: "Test")
present(vc, animated: true, completion: nil)
}
}
I would like to call a function in my buttonViewController if the user presses the perform button and than i want to perform a segue to my buttonViewController.
This is my ButtonViewController:
import UIKit
class ButtonViewController: UIViewController, ViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
ViewController().delegate = self
print("test")
// Do any additional setup after loading the view.
}
/*
// 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.destination.
// Pass the selected object to the new view controller.
}
*/
#IBAction func performsegue(_ sender: UIButton) {
}
func printTest(message: String) {
print(message + "\(self)")
}
}
I've already tried to perform a segue that first viewDidLoad is called by my ButtonViewController, then to go back to the ViewController and then call the perform method again via a button to then see whether delegate is still nil, and yes it was unfortunately still nil.
Can somebody help me with this Problem?
Best regards!
The delegate pattern is confusing at first for a lot of people. I find that most of my students tend to try and do it backward at first, which is I think what is going on here as well. Usually in the delegate pattern there is one view controller (A) that presents another (B). Typically some action on B should trigger some function on A (in your case, perhaps a button press on B triggers causes A to print something).
In this scenario you would have two subclasses of UIViewController: AViewController and BViewController and a protocol BViewControllerDelegate. It would be setup as follows:
The protocol BViewControllerDelegate would have the function you want to be called on AViewController when the button in BViewController is pressed.
AViewController would conform to this protocol and implement this function.
In BViewController you would have your delegate property defined: weak var delegate: BViewControllerDelegate?.
This property would be set on an instance of BViewController by an instance of AViewController to itself (the instance of AViewController) during the presentation of BViewController.
The instance of BViewController would invoke the function on its delegate property in response to a button press.
class AViewController: UIViewController, BViewControllerDelegate {
// This is 4, this segue action is invoked by a storyboard segue in the storyboard and is responsible for setting up the destination view controller and configuring it as needed (i.e., setting its delegate property)
#IBSegueAction func makeBViewController(_ coder: NSCoder) -> BViewController {
let bViewController = BViewController(coder: coder)!
bViewController.delegate = self
return bViewController
}
// Here we accomplish 2 (and also above where we declare conformance to the protocol)
func bViewControllerDidPerformAction(viewController: BViewController, message: String) {
print(message)
}
}
protocol BViewControllerDelegate: AnyObject {
// Here we accomplish 1
func bViewControllerDidPerformAction(viewController: BViewController, message: String)
}
class BViewController: UIViewController {
// Here is 5
#IBAction func buttonPressed(_ sender: Any) {
delegate?.bViewControllerDidPerformAction(viewController: self, message: "Test Message")
}
// This is 3
weak var delegate: BViewControllerDelegate?
}
I'm not 100% clear on what you're trying to do in your code, but I believe your ViewController should be setup like AViewController and your ButtonViewController should be setup like BViewController. Also, the line of code ViewController().delegate = self does nothing, because it creates a new instance of ViewController and sets its delegate, but this new instance is immediately deallocated because it is not actually the one being used anywhere else.
Protocol / Delegate pattern is used to allow an instantiated controller (or other object) to communicate back to the class that created it.
So, in your scenario, you want ViewController to conform to ViewControllerDelegate ... when you instantiate ButtonViewController (which has a ViewControllerDelegate delegate var), you assign that ButtonViewController's delegate to self (ViewController).
Now, ButtonViewController can call protocol functions via its delegate.
protocol ViewControllerDelegate {
func printTest(message: String)
}
// make ViewController conform to ViewControllerDelegate
class ViewController: UIViewController, ViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func perform(_ sender: UIButton) {
let vc = storyboard?.instantiateViewController(withIdentifier: "vc") as! ButtonViewController
// set self as the delegate in ButtonViewController
vc.delegate = self
present(vc, animated: true, completion: nil)
}
func printTest(message: String) {
print("printTest in delegate:", message)
}
}
class ButtonViewController: UIViewController {
var delegate: ViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
print("test")
}
#IBAction func buttonTap(_ sender: UIButton) {
// call the delegate
delegate?.printTest(message: "Sending to delegate!")
}
}
Edit -- here is a complete example to run. "Main" view controller has a button and a label. Tap the button to present the ButtonVC. Button view controller has a text field and a "Save and Close" button. Enter some text in the text field, tap the Save button, and the text will be sent to the Main controller via the ViewControllerDelegate.
No #IBOutlet or #IBAction connections needed -- just assign a blank view controller's custom class to ViewController:
// make ViewController conform to ViewControllerDelegate
class ViewController: UIViewController, ViewControllerDelegate {
let btn: UIButton = {
let v = UIButton()
v.setTitle("Show Button VC", for: [])
v.backgroundColor = .systemRed
v.setTitleColor(.white, for: .normal)
v.setTitleColor(.lightGray, for: .highlighted)
return v
}()
let label: UILabel = {
let v = UILabel()
v.backgroundColor = .green
v.numberOfLines = 0
v.text = "From ButtonVC:\n"
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
[label, btn].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
label.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
btn.addTarget(self, action: #selector(showButtonsVC(_:)), for: .touchUpInside)
}
#objc func showButtonsVC(_ sender: UIButton) {
// instantiate Button View Controller
let vc = ButtonViewController()
// set self as the delegate in ButtonViewController
vc.delegate = self
// present it
present(vc, animated: true, completion: nil)
}
func printTest(message: String) {
print("Delegate received:", message)
label.text = "From ButtonVC:\n" + message
}
}
class ButtonViewController: UIViewController {
var delegate: ViewControllerDelegate?
let textField: UITextField = {
let v = UITextField()
v.borderStyle = .roundedRect
return v
}()
let btn: UIButton = {
let v = UIButton()
v.setTitle("Save and Close", for: [])
v.backgroundColor = .systemRed
v.setTitleColor(.white, for: .normal)
v.setTitleColor(.lightGray, for: .highlighted)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
[textField, btn].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textField.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
textField.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
btn.addTarget(self, action: #selector(buttonTap(_:)), for: .touchUpInside)
}
#IBAction func buttonTap(_ sender: UIButton) {
// call the delegate
let str = textField.text ?? "No text entered..."
delegate?.printTest(message: str)
dismiss(animated: true, completion: nil)
}
}

Presenting a UIViewController from an embedded ViewController keeping its bounds

I try to present a ViewController modally from a parent ViewController that's already embedded in a UIView. But whenever I try presenting it, it just pops over the entire window. I want it to be equally sized as its parent. I do not need an animation for the transition. The only thing that worked so far is to again add it's view manually and manipulate the constraints via auto layout. Therefore I would present the controller by hiding its corresponding view. But compared to present that seems more like a workaround than a solution. So far I tried some options on modalPresentationStyle, but none of them seem to work for me. Glad for any help.
Cheers!
class ViewController: UIViewController {
lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
lazy var viewController: ParentViewController = {
let viewController = ParentViewController()
return viewController
}()
override func viewDidLoad() {
super.viewDidLoad()
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
containerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.75).isActive = true
viewController.view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(viewController.view)
viewController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
viewController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
viewController.view.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
viewController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
viewController.didMove(toParent: self)
}
}
class ParentViewController: UIViewController {
#IBAction func buttonPressed(_ button: UIButton) {
let childController = UIViewController() // This View Controller should fit it´s parents size. Instead it´s just presented normally and fits the window
present(childController, animated: false)
}
}
Edit
Derived from the community answers I implemented following solution:
extension UIViewController {
func open(_ viewControllerToPresent: UIViewController, in view: UIView? = nil, animated: Bool, duration: TimeInterval = 0.25, completion: (() -> Void)? = nil) {
viewControllerToPresent.view.isHidden = true
open(viewControllerToPresent, in: view)
if animated {
viewControllerToPresent.view.alpha = 0.0
viewControllerToPresent.view.isHidden = false
UIView.animate(withDuration: duration) {
viewControllerToPresent.view.alpha = 1.0
} completion: { (_) in
completion?()
}
}
else {
viewControllerToPresent.view.isHidden = false
completion?()
}
}
func open(_ viewControllerToPresent: UIViewController, in view: UIView? = nil) {
let parentView = view ?? self.view!
addChild(viewControllerToPresent)
viewControllerToPresent.view.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(viewControllerToPresent.view)
viewControllerToPresent.view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).isActive = true
viewControllerToPresent.view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor).isActive = true
viewControllerToPresent.view.topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
viewControllerToPresent.view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor).isActive = true
viewControllerToPresent.didMove(toParent: self)
}
func close(animated: Bool, duration: TimeInterval = 0.25, completion: (() -> Void)? = nil) {
if animated {
UIView.animate(withDuration: duration) {
self.view.alpha = 0.0
} completion: { (_) in
self.close()
completion?()
}
}
else {
close()
completion?()
}
}
func close() {
view.removeFromSuperview()
willMove(toParent: nil)
removeFromParent()
}
}

Passing a value to a previous view controller doesn't reflect the change

I have MainViewController and DetailViewController that are stacked together by a navigation controller. I want to pass a value from DetailViewController back to the previous controller, which is MainViewController.
First, I tried it with UINavigationControllerDelegate:
class DetailViewController: UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
(viewController as? MainViewController)?.myClass = myClass
}
}
which was to be called as DetailViewController is popped:
_ = navigationController?.popViewController(animated: true)
But, the new value doesn't get reflected on MainViewController:
class MainViewController: UIViewController {
var myClass: MyClass
private lazy var commentLabel: UILabel = {
let comment = UILabel()
comment.text = myClass.comment
comment.numberOfLines = 0
return comment
}()
}
even though when I log myClass in MainViewController, I can see that it's being passed properly.
I also tried it with a property observer so that DetailViewController can pass it to a temporary property observer instead:
var temp: MyClass? {
willSet(newValue) {
myClass = newValue
}
}
but, the view controller's interface still doesn't change.
Finally, I tried creating a delegate in MainViewController:
protocol CallBackDelegate {
func callBack(value: MyClass)
}
where the function simply passes the argument:
func callBack(value: MyClass) {
myClass = value
}
I set the delegate to self:
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
and invoking the function in DetailViewController:
delegate?.callBack(value: MyClass)
but, still doesn't update the interface. It seems as though passing the value isn't the issue, but having it be reflected is.
This is an example of using the protocol / delegate pattern. It's about as basic as it gets...
Start a new single-view project
add the code below
Set the class of the default view controller to MainViewController
embed it in a Navigation Controller
run the app
Then:
Tap the button labeled "Push to next VC"
Enter some text in the "Edit Me" field
Tap the "Pop back to previous VC"
See that the label has been updated with your entered text.
protocol CallBackDelegate: class {
func callback(_ val: String)
}
class MainViewController: UIViewController, CallBackDelegate {
let btn = UIButton()
let theLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
btn.translatesAutoresizingMaskIntoConstraints = false
theLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(theLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
theLabel.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
theLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
theLabel.backgroundColor = .yellow
btn.backgroundColor = .red
theLabel.text = "Default text"
btn.setTitle("Push to next VC", for: [])
btn.addTarget(self, action: #selector(self.pushButtonTapped(_:)), for: .touchUpInside)
}
#objc func pushButtonTapped(_ sender: Any?) -> Void {
let vc = DetailViewController()
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func callback(_ val: String) {
theLabel.text = val
}
}
class DetailViewController: UIViewController {
weak var delegate: CallBackDelegate?
let textField = UITextField()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
btn.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(textField)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textField.centerXAnchor.constraint(equalTo: g.centerXAnchor),
textField.widthAnchor.constraint(equalToConstant: 200.0),
btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
textField.backgroundColor = .yellow
textField.borderStyle = .roundedRect
btn.backgroundColor = .blue
textField.placeholder = "Edit me"
btn.setTitle("Pop back to previous VC", for: [])
btn.addTarget(self, action: #selector(self.popButtonTapped(_:)), for: .touchUpInside)
}
#objc func popButtonTapped(_ sender: Any?) -> Void {
if let s = textField.text {
delegate?.callback(s)
}
self.navigationController?.popViewController(animated: true)
}
}
Doesn't seem that you are updating the UILabel value anyhow
var myClass: MyClass? {
didSet {
self.commentLabel.text = myClass?.comment
}
}
You have to update the label text itself, right now it's constant with the first load data

How to fix subview not showing in child view controller when added to a parent view controller

I'm trying to add a child view controller to a parent view controller in a swift ios application, but when I add the child view controller, the activityIndicatorView doesn't appear. What could I be missing?
Here is a snippet that can be tried in a playground:
import PlaygroundSupport
import Alamofire
class LoadingViewController: UIViewController {
private lazy var activityIndicator = UIActivityIndicatorView(style: .gray)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(activityIndicator)
NSLayoutConstraint.activate([
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// We use a 0.5 second delay to not show an activity indicator
// in case our data loads very quickly.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) { [weak self] in
self?.activityIndicator.startAnimating()
}
}
}
// methods for adding and removing child view controllers.
extension UIViewController {
func add(_ child: UIViewController, frame: CGRect? = nil) {
addChild(child)
if let frame = frame {
child.view.frame = frame
}
view.addSubview(child.view)
child.didMove(toParent: self)
}
func remove() {
// Just to be safe, we check that this view controller
// is actually added to a parent before removing it.
guard parent != nil else {
return
}
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
}
class MyViewController : UITabBarController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.text = "Hello World!"
label.textColor = .black
view.addSubview(label)
self.view = view
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let loadingViewController = LoadingViewController()
add(loadingViewController, frame: view.frame)
AF.request("http://www.youtube.com").response { response in
print(String(describing: response.response))
loadingViewController.remove()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
The loadingViewController view fills the parent view, and i've tried to change the background colour at different points and that works. but activityIndicator or any other subview i try to add just doesn't appear.
Try add the line
activityIndicator.startAnimating()
in your viewDidLoad() method from your LoadingViewController class

Resources