I spent around 5 hours searching on StackOverFlow about how to implement this and I still not found a solution yet!
I have created a new iOS Swift app just to explain you what exactly I am trying to resolve.
First, I have a UIButton on main storyboard and when you click on it, it creates a new object from class WebViewAsPopup which creates programmatically a new WebView and a background as UIView with alpha 0.5, add some styling and then it add them to the main view controller ViewController as a popup.
Secondly, I couldn't set a delegate to those WebView's to handle UIWebViewDelegate and listen to both webViewDidStartLoad and webViewDidFinishLoad
Here's my code:
ViewController.swift
//
// ViewController.swift
// MySimpleApp
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var showPopupBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func showPopupTapped(_ sender: Any) {
let popup = WebViewAsPopup()
popup.showWebViewPopup(on: self)
}
}
WebViewAsPopup.swift
//
// WebViewAsPopup.swift
// MySimpleApp
//
import WebKit
class WebViewAsPopup: NSObject, UIWebViewDelegate {
func showWebViewPopup(on controller: UIViewController) {
// Popup background
let bg = UIView()
bg.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
bg.layer.backgroundColor = UIColor.black.withAlphaComponent(0.5).cgColor
// Webview sizing
let width = UIScreen.main.bounds.width - 60
let height = UIScreen.main.bounds.height - 200
let x = UIScreen.main.bounds.width/2 - width/2
let y = UIScreen.main.bounds.height/2 - height/2
// Webview stuff
let webView = UIWebView()
webView.frame = CGRect(x: x, y: y, width: width, height: height)
let url = URL(string: "https://google.com")
let request = URLRequest(url: url!)
// webView.delegate = self << it crash here
webView.loadRequest(request)
// Styling webview popup
webView.layer.borderWidth = 1
webView.layer.borderColor = UIColor.black.cgColor
webView.layer.masksToBounds = true
webView.layer.cornerRadius = 10
// Add bg then webview to main view controller
controller.view.addSubview(bg)
controller.view.addSubview(webView)
}
func webViewDidStartLoad(_ webView: UIWebView)
{
print("#webViewDidStartLoad!") // << it doesn't fire :(
}
func webViewDidFinishLoad(_ webView: UIWebView)
{
print("#webViewDidFinishLoad!") // << it doesn't fire :(
}
}
As I noted in my comments, you need to save the popup object somewhere in order to retain it. If not (as in your example code), then it will be deallocated as soon as the action method showPopupTapped(_:) is completed.
Try this:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var showPopupBtn: UIButton!
let popup = WebViewAsPopup()
#IBAction func showPopupTapped(_ sender: Any) {
self.popup.showWebViewPopup(on: self)
}
}
Bonus Note:
If you're wondering why you don't need to do the same to the background view and web view in your popup object, that's because views automatically retain their subviews.
So, once you add your background view and web view as subviews to a parent view, their retain count goes up and will survive their current scope.
Related
I have used the code as below. But it only blur one position. Now I want to blur multiple positions at once
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btn_blur(_ sender: Any) {
// 1
let darkBlur = UIBlurEffect(style: .dark)
// 2
let blurView = UIVisualEffectView(effect: darkBlur)
blurView.frame = CGRect(x: 20, y: 30, width: 50, height: 50)
// 3
imageView.addSubview(blurView)
}
}
You could store an array of frames in your view controller, and fill that with your server response. Then use those frames to create the blur views.
import UIKit
class ViewController: UIViewController {
var frames = [CGRect]() // load this from your server data
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btn_blur(_ sender: Any) {
let darkBlur = UIBlurEffect(style: .dark)
for frame in frames {
let blurView = UIVisualEffectView(effect: darkBlur)
blurView.frame = frame
imageView.addSubview(blurView)
}
}
}
I would like to play a sound file when a new page comes on the screen.
So far I did that to have the pageControler working with an array of images.
Can someone tell me where I have to put my code in order to play an array of sound? So that the sound1 will be played when the image1 comes on the screen.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
var images: [String] = ["1", "2", "3", "4", "5"]
var frame = CGRect(x:0,y:0, width:0, height:0)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pageControl.numberOfPages = images.count
for index in 0..<images.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let imgView = UIImageView(frame: frame)
imgView.image = UIImage(named: images[index])
self.scrollView.addSubview(imgView)
}
scrollView.contentSize = CGSize(width:(scrollView.frame.size.width * CGFloat(images.count)), height: scrollView.frame.size.height)
scrollView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
}
I don't see a direct way to get called when the selected page changes. You could add code in your page view controller's setViewControllers(_:direction:animated:completion:) method that figures out which page is active and plays the appropriate sound.
If you use a page control you could probably also subclass UIPageControl and use a didSet on the currentPage property to figure out which sound to play.
Edit:
Just add a new file, make it a Cocoa touch class, and make it a subclass of UIPageControl. Name it CustomPageControl. Then your implementation can be as simple as this:
import UIKit
class CustomPageControl: UIPageControl {
override var currentPage: Int {
didSet {
//Your code to play sounds based on selected index could go
//here, or broadcast a notification that your view controller
//would listen for
print("New page index = \(currentPage)")
}
}
}
Then just select the page control on your UIPageViewController, select the "Identity Inspector", and change the class of the page control to your custom CustomPageControl class. Once you've done that, whenever your page index changes, the didSet method above will be called.
I created a new file with the following class:
import Foundation
import UIKit
var Feld = classFeld()
class classFeld {
let button = UIButton()
func createButton() -> UIButton {
button.frame = CGRect(x: 10, y: 50, width: 200, height: 100)
button.backgroundColor=UIColor.black
button.addTarget(self, action: #selector(ButtonPressed(sender:)), for: .touchUpInside)
return button
}
#objc func ButtonPressed(sender: UIButton!) {
button.backgroundColor=UIColor.red
}
}
And this is my ViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
mainview.addSubview(Feld.createButton())
self.view.addSubview(mainview)
}
var mainview=UIView()
}
When I start the app a black button is created but when I click on it it doesnt color red.
If I add the button instead of
mainview.addSubview(Feld.createButton())
to
self.view.addSubview(Feld.createButton())
it works and the button turns red.
May I anyone explain me why? It should make no difference if I add something to self.view or to a subview which is then added to self.view?
Because you need to give it a frame and add it to self.view
var mainview = UIView()
This is because you are just initializing a UIView without giving it any frame and adding it to the main view.
You need to give frame to your mainview also. For example :
class ViewController: UIViewController {
var mainview = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mainview.frame = CGRect(x: 10, y: 50, width: 300, height: 300)
self.view.addSubview(mainview)
mainview.addSubview(Feld.createButton())
}
}
Here is changes on ViewController class and working fine:
class ViewController: UIViewController {
var mainview: UIView!
override func viewDidLoad() {
super.viewDidLoad()
mainview = UIView(frame: self.view.bounds)
mainview.addSubview(Feld.createButton())
self.view.addSubview(mainview)
}
}
I have watched this tutorial
Snapchat Like Layout using View Controller Theme
then I embed one of the three controllers in Navigation Controller but it didn't work then i have tried to add it on the view controller how had the scroll view also didn't work + gives me overlap I didn't tried it in code tho but I don't think this is the cause , this is the code
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let V1 = self.storyboard?.instantiateViewController(withIdentifier: "V1") as UIViewController!
self.addChildViewController(V1!)
self.scrollView.addSubview((V1?.view)!)
V1?.didMove(toParentViewController: self)
V1?.view.frame = scrollView.bounds
let V2 = self.storyboard?.instantiateViewController(withIdentifier: "V2") as UIViewController!
self.addChildViewController(V2!)
self.scrollView.addSubview((V2?.view)!)
V2?.didMove(toParentViewController: self)
V2?.view.frame = scrollView.bounds
var V2Frame: CGRect = V2!.view.frame
V2Frame.origin.x = self.view.frame.width
V2?.view.frame = V2Frame
let V3 = self.storyboard?.instantiateViewController(withIdentifier: "V3") as UIViewController!
self.addChildViewController(V3!)
self.scrollView.addSubview((V3?.view)!)
V3?.didMove(toParentViewController: self)
V3?.view.frame = scrollView.bounds
var V3Frame: CGRect = V3!.view.frame
V3Frame.origin.x = 2 * self.view.frame.width
V3?.view.frame = V3Frame
self.scrollView.contentSize = CGSize(width: self.view.frame.width * 3, height: self.view.frame.height)
self.scrollView.contentOffset = CGPoint(x: self.view.frame.width * 1 , y: self.view.frame.height)
}
how the code works ??
1- first I have added scroll view to a view controller
2- then inside the scroll view I have added 3 view controller and all the 3 controllers has own class like normal view controllers
3- the scroll View added the 3 controllers on its view by this code on the top
all the 3 controllers inheriting from own class and everything is wor fine
if anyone can help or need more description just ask
try below
Create ViewController in Storyboard or XIB and add ScrollView and PageControl
My Class as below:
class ScrollContentViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.pageControl.currentPage = 0
self.pageControl.numberOfPages = 4
var originX = CGFloat(0)
let width = scrollView.frame.size.width
let height = scrollView.bounds.size.height
for x in 0 ..< 4 {
let V1 = UIViewController()
self.scrollView.addSubview(V1.view)
V1.view.frame = CGRect(x: originX, y: 0, width: width, height: height)
V1.view.backgroundColor = x % 2 == 0 ? UIColor.lightGray : UIColor.darkGray
self.addChildViewController(V1)
originX += width
}
self.scrollView.contentSize = CGSize(width: originX, height: height)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let x = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
self.pageControl.currentPage = x
}
/*
// 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.
}
*/
}
I am trying to create scrollable imageview. Currently using swift 4.0. While running I am getting "has no initializers" error. I am not sure what code brought this error. I am new to swift and I am unable to track what is going on.
Class 'ProductDetailViewController' has no initializers I am getting this error.
Can any one please tell me how to fix this error.
Entire view controller code is given below. Thanks in advance
import UIKit
import SwiftyJSON
class ProductDetailViewController: UIViewController,UIScrollViewDelegate {
//Outlers
#IBOutlet weak var scrollView: UIScrollView!
//Global variables
var productDict : JSON = []
var arrayOfProductImageUrls : Array<Any> = []
var zoomScroll : UIScrollView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.initialSetup()
self.scrollViewSetup()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// 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: - Scrollview Setup
func initialSetup() {
self.scrollView.delegate = self
}
func scrollViewSetup() {
let productsArray : Array = self.arrayOfProductImageUrls;
self.scrollView.backgroundColor = UIColor.white
var frame : CGRect = CGRectMake(0, 0, self.scrollView.frame.size.width, 360)
for (index, value) in productsArray.enumerated() {
//Imageview Setup
let productImageView : UIImageView
let width : NSInteger = Int(frame.width)
productImageView.frame.origin.x = CGFloat(Int (width * index))
productImageView.frame.origin.y = 0
//Scrollview Setup
zoomScroll.frame = frame;
zoomScroll.backgroundColor = UIColor.clear
zoomScroll.showsVerticalScrollIndicator = false
zoomScroll.showsHorizontalScrollIndicator = false
zoomScroll.delegate = self
zoomScroll.minimumZoomScale = 1.0
zoomScroll.maximumZoomScale = 6.0
zoomScroll.tag = index
zoomScroll.isScrollEnabled = true
self.scrollView.addSubview(zoomScroll)
//Setting image
let imageUrl : URL = (productsArray[index] as AnyObject).url
productImageView.sd_setImage(with: imageUrl)
if index < productsArray.count {
productImageView.frame = zoomScroll.bounds
productImageView.contentMode = UIViewContentMode.redraw
productImageView.clipsToBounds = true
productImageView.backgroundColor = UIColor.clear
zoomScroll.addSubview(productImageView)
}
self.scrollView.contentSize = CGSizeMake(CGFloat(frame.origin.x) + CGFloat(width), productImageView.frame.size.height)
}
}
//Mark : Custom methods
func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
return CGRect(x: x, y: y, width: width, height: height)
}
func CGSizeMake(_ width: CGFloat, _ height: CGFloat) -> CGSize {
return CGSize(width: width, height: height)
}
}
#Saurabh Prajapati. Nice and quick catch.
For more, The Swift Programming Language states
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
You can set an initial value for a stored property within an
initializer, or by assigning a default property value as part of the
property’s definition.
Let me explain more, the problem was with scrollView property that does not have a default value. As above mentioned, all variables in Swift must always have a value or nil (make it optional). When you make it optional, you allow it to be nil by default, removing the need to explicitly give it a value or initialize it.
Here we are using ! optional so the default value will be nil
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
}
Setting nil as the default value
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView! = nil
}
Setting the default value, creating an object now it is not nil.
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView = UIScrollView()
}
But we can't-do like that, we are not setting the default value here.
class ViewController : UIViewController {
#IBOutlet weak var scrollView: UIScrollView
}
I hope it will help.
I had the same echo
it was so easy to solve it by using ! after any variable like this
var ref: DatabaseReference!