I am trying to show this simple ViewController in iPad Playgrounds with this code:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width/2, height: 100))
view1.backgroundColor = .red
view.addSubview(view1)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
You can see that, even though it says view.frame.width/2 for the frame of View1, the view still looks like this:
What is wrong? Thank you.
This is the solution I used which got it to work. In Swift Playgrounds, the layout is created in the viewDidLayoutSubview method so all frames should be created there.
You can set your preferred size after you init your ViewController:
import UIKit
import PlaygroundSupport
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width/2, height: 100))
view1.backgroundColor = .red
view.addSubview(view1)
}
}
let vc = VC()
vc.preferredContentSize = CGSize(width: 768, height: 1024)
PlaygroundPage.current.liveView = vc
Playground with preferredContentSize
Playground without preferredContentSize
Related
I am following a tutorial to create a swift app that adds a number of views to a scroll view and then allows me to scroll between them. I have the app working and understand it for the most part. When I change the orientation of the device the views width don't get updated so I have parts of more than one view controller on the screen? Does anybody know how to fix this? From my understanding I need to call something in the viewsDidTransition method to redraw the views. Any help would be greatly appreciated. Thanks!
Here is what I have so far:
import UIKit
class ViewController: UIViewController {
private let scrollView = UIScrollView()
private let pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.numberOfPages = 5
pageControl.backgroundColor = .systemBlue
return pageControl
}()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
pageControl.addTarget(self,
action: #selector(pageControlDidChange(_:)),
for: .valueChanged)
scrollView.backgroundColor = .red
view.addSubview(scrollView)
view.addSubview(pageControl)
}
#objc private func pageControlDidChange(_ sender: UIPageControl){
let current = sender.currentPage
scrollView.setContentOffset(CGPoint(x: CGFloat(current) * view.frame.size.width,
y: 0), animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
pageControl.frame = CGRect(x: 10, y: view.frame.size.height - 100, width: view.frame.size.width - 20, height: 70)
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height - 100)
if scrollView.subviews.count == 2 {
configureScrollView()
}
}
private func configureScrollView(){
scrollView.contentSize = CGSize(width: view.frame.size.width*5, height: scrollView.frame.size.height)
scrollView.isPagingEnabled = true
let colors: [UIColor] = [.systemRed, .systemGray, .systemGreen, .systemOrange, .systemPurple]
for x in 0..<5{
let page = UIView(frame: CGRect(x: CGFloat(x) * view.frame.size.width, y: 0, width: view.frame.size.width, height: scrollView.frame.size.height))
page.backgroundColor = colors[x]
scrollView.addSubview(page)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
print("hello world")
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(floorf(Float(scrollView.contentOffset.x) / Float(scrollView.frame.size.width)))
}
}
i want to implement a singleton spinner, because any app that has a api needs a spinner. well I can code a spinner custom, but my problem is that I should code the next line ever if I want to show it.
let rosetaGIF = UIImage(named: "wheel.png")
let ind = MyIndicator(frame: CGRect(x: 0, y: 0, width: (rosetaGIF?.size.width)!/2, height: (rosetaGIF?.size.height)!/2), image: rosetaGIF!)
view.addSubview(ind)
view.alpha = 0.5
ind.startAnimating()
that's not good, because I must put this lines every time that I want to show the spinner, well, my spinner class is the next. I'm using swift 4.2
import UIKit
class MyIndicator: UIActivityIndicatorView {
let loadingView = UIView(frame: (UIApplication.shared.delegate?.window??.bounds)!)
let imageView = UIImageView()
let sizeView = UIViewController()
init(frame: CGRect, image: UIImage) {
super.init(frame: frame)
imageView.frame = bounds
imageView.image = image
imageView.contentMode = .scaleAspectFit
imageView.center = CGPoint(x: sizeView.view.frame.width/2, y: sizeView.view.frame.height/2)
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(imageView)
}
required init(coder: NSCoder) {
fatalError()
}
override func startAnimating()
{
isHidden = false
rotate()
}
override func stopAnimating()
{
isHidden = true
removeRotation()
}
private func rotate() {
let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = NSNumber(value: Double.pi * 1)
rotation.duration = 1
rotation.isCumulative = true
rotation.repeatCount = Float.greatestFiniteMagnitude
self.imageView.layer.add(rotation, forKey: "rotationAnimation")
}
private func removeRotation() {
self.imageView.layer.removeAnimation(forKey: "rotationAnimation")
}
}
what should I do for that the spinner will be singleton?
thanks
I suggest you create a util class and make that class Singleton.
import UIKit
class Util {
static let shared = Util()
private init(){}
var loader: MyIndicator?
func showLoader(view: UIView){
hideLoader()
let rosetaGIF = UIImage(named: "wheel.png")
loader = MyIndicator(frame: CGRect(x: 0, y: 0, width: (rosetaGIF?.size.width)!/2, height: (rosetaGIF?.size.height)!/2), image: rosetaGIF!)
view.addSubview(loader!)
view.alpha = 0.5
loader?.startAnimating()
}
func hideLoader(){
loader?.stopAnimating()
loader?.removeFromSuperview()
}
}
How to use this class
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Util.shared.showLoader(view: view) // for showing the loader
Util.shared.hideLoader() // for hiding the loader
}
}
Create a Base Controller with your spinner, then inherit other Controllers from you Base Controller and show the spinner whenever you want.
class BaseViewController: UIViewController {
var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: (UIScreen.main.bounds.width-40)/2, y: (UIScreen.main.bounds.height-40)/2, width: 40, height: 40))
activityIndicator.transform = CGAffineTransform(scaleX: 2, y: 2)
activityIndicator.color = .darkGray
view.addSubview(activityIndicator)
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating() // you can show the spinner wherever you want.
}
}
hope this will help to you.
I try to access the function "addNavBar()" from, my helper class, but when I run the emulator, no view is shown on HomeViewController.
Swift 4
HomeViewController.swift
class HomeController: UIViewController {
let NavBar = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
NavBar.addNavBar()
}
}
NavigationBarHelper.swift
class NavigationBarHelper: UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
self in NavigationBarHelper is not the same object as the view in the view controller. Pass the VC's view as a parameter. There is no need to make NavigationBarHelper a subclass of UIView (in fact it could also be a struct).
class NavigationBarHelper {
func addNavBar(to view: UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
please also stick to naming conventions
class HomeController: UIViewController {
let navBarHelper = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
navBarHelper.addNavBar(to: self.view)
}
Instead of creating an object every usage
let NavBar = NavigationBarHelper()
I think it's more robust to make it static like this
class NavigationBarHelper: UIView {
static func addNavBar(view:UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
and call it directly without object creation
NavigationBarHelper.addNavBar(view: self.view)
A Better Alternative for this would be an extension so you don't have to create the special class for this
extension UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
And in you UIViewController you can simply write this without creating an object of your helper.
self.view.addNavBar()
I would like to know how to create a horizontal scrollview in swift programmatically without using storyboards, with a page controller.
import UIKit
class SecondViewController: UIViewController, UIScrollViewDelegate {
var scrollview = UIScrollView()
var imageview = UIImageView()
var view1 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
imageview.frame = CGRect(x: 0, y: 0, width: view.frame.size.width , height: 1000)
imageview.image = UIImage(named: "image")
scrollview.delegate = self
scrollview.contentSize = CGSize(width: imageview.frame.width, height: imageview.frame.height)
scrollview.backgroundColor = UIColor.red
imageview.backgroundColor = UIColor.green
self.scrollview.addSubview(imageview)
view.addSubview(scrollview)
scrollview.translatesAutoresizingMaskIntoConstraints = false
scrollview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
scrollview.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollview.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollview.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Do any additional setup after loading the view.
}
Try This
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
var scrollView: UIScrollView!
let textData: String = "Just to add onto the already great answers, you might want to add multiple labels in your project so doing all of this (setting size, style etc) will be a pain. To solve this, you can create a separate UILabel class."
override func viewDidLoad() {
super.viewDidLoad()
let textLable = UILabel(frame: CGRect(x: 0, y: 0, width: 1000, height: 200))
textLable.text = textData
self.scrollView = UIScrollView()
self.scrollView.delegate = self
self.scrollView.contentSize = CGSize(width: textLable.frame.width, height: textLable.frame.height)
scrollView.addSubview(textLable)
view.addSubview(scrollView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm trying to understand how addArrangedSubview is working so I tried with a dummy project. Here's my VC :
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var stackOutlet: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let newView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
newView.backgroundColor = UIColor.darkGray
let newView2 = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
newView2.backgroundColor = UIColor.blue
stackOutlet.addArrangedSubview(newView)
stackOutlet.addArrangedSubview(newView2)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I launch the app, nothing is appearing. I think I have to set some constraints in the stackView?
Ok so for everyone sakes : you need to use this =
mapView.heightAnchor.constraint(equalToConstant: 200).isActive = true
mapView.widthAnchor.constraint(equalToConstant: 200).isActive = true