Move UIView from ViewController to Window - ios

Thanks for taking the time to read thus. So basically, I have a UIView in my UIViewController. I want a user to be able to press a button and then the UIView moves from my UIViewController to the my application's window so that the UIView will be above all UIViewControllers. The only thing I could think of doing was
class ViewController: UIViewController {
var window = UIApplication.shared.keyWindow!
var view = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(view)
}
func tappedAction() {
window.bringSubview(toFront: view)
}
}
but that didn't work. Does anyone have any ideas on how to accomplish this?

You cannot just bring the subview that's in your UIViewController to the front of your UIWindow.
You need to:
Remove the UIView from the UIViewController.
Add the UIView to the main UIWindow.
I chose to do this in this way:
import UIKit
class ViewController: UIViewController {
var customView: UIView!
// Load the main view of the UIViewController.
override func loadView() {
view = UIView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Load the custom view that we will be transferring.
self.customView = UIView(frame: .init(x: 100, y: 250, width: 250, height: 250))
self.customView.backgroundColor = .red
view.addSubview(customView)
// Transfer the view. Call this method in your trigger function.
transfer(self.customView)
}
func transfer(_ view: UIView) {
// Remove the view from the UIViewController.
view.removeFromSuperview()
// Add the view to the UIWindow.
UIApplication.shared.windows.first!.addSubview(view)
}
}

You must set frame fro view at var view = UIView()
then you should add to window window.addSubview(view)

If your view is added on window then window.bringSubview(toFront: view) will work otherwise it will not.
If your view is added on window then you can use bringSubview(toFront:) like that:
Example:
let window = UIApplication.shared.keyWindow!
let view1 = UIView(frame: CGRect(x: window.frame.origin.x, y: window.frame.origin.y, width: window.frame.width, height: window.frame.height))
window.addSubview(view1);
view1.backgroundColor = UIColor.black
let view2 = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
view2.backgroundColor = UIColor.white
window.addSubview(view2)
UIApplication.shared.keyWindow!.bringSubview(toFront: view1)
So you need to add your view in window:
window.addSubview(view)

Related

Add button overlay on UITableViewController with use static cells

I try to add button overlay on UITableViewController with static cells. But i get this result, button is working, but i not see result of search:
I'm trying to get this result:
I want to button was always at the bottom regardless of scrolling up or down.
In my code i use framework InstantSearch:
import UIKit
import InstantSearch
import WARangeSlider
class SearchTableViewController: UITableViewController {
#IBOutlet weak var resultButton: StatsButtonWidget!
override func viewDidLoad() {
super.viewDidLoad()
resultButton.frame = CGRect(x: 0, y: 0, width: 150, height: 60)
navigationController?.view.addSubview(resultButton)
InstantSearch.shared.registerAllWidgets(in: self.view)
LayoutHelpers.setupResultButton(button: resultButton)
resultButton.addTarget(self, action: #selector(resultButtonClicked), for: .touchUpInside)
}
}
How can i add button overlay on bottom in UITableViewController? Me need use only UITableViewController, not UIViewController with TableView.
You could directly add the button to the UITableView without AutoLayout, and make sure TableView's delegate is the controller, like:
override func viewDidLoad() {
super.viewDidLoad()
self.button.frame = CGRect(x: 0, y: self.tableView.frame.size.height - 50, width: self.tableView.frame.width, height: 50)
self.tableView.addSubview(self.button)
self.tableView.delegate = self
}
Then you are able to fix the button's position by UIScrollView delegate (UITableViewDelegate inherited from this) while TableView is scrolling:
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView == self.tableView) {
let originY = scrollView.frame.size.height - self.button.frame.size.height + scrollView.contentOffset.y
self.button.frame = CGRect(x: 0, y: originY, width: scrollView.frame.width, height: self.button.frame.size.height)
}
}
Alternatively, if you want to position the button by AutoLayout, just define a NSLayoutConstraint property, and bind it to button's bottom space constraint to its super view. Then adjust the constraint's constant value by same mechanism in scrollViewDidScroll function.
You can just add an view at the bottom of your tableview.
override func viewDidLoad() {
super.viewDidLoad()
addResultButtonView()
}
private func addResultButtonView() {
let resultButton = UIButton()
resultButton.backgroundColor = .red
resultButton.setTitle("Hello", for: .normal)
tableView.addSubview(resultButton)
// set position
resultButton.translatesAutoresizingMaskIntoConstraints = false
resultButton.leftAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.leftAnchor).isActive = true
resultButton.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor).isActive = true
resultButton.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor).isActive = true
resultButton.widthAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.widthAnchor).isActive = true
resultButton.heightAnchor.constraint(equalToConstant: 50).isActive = true // specify the height of the view
}

UIViewController hidden behind UINavigationController but should be placed below

I'm working on a app where I've got a NavigationBar at the top and added a UIViewController as an RootViewController.
Now my plan was to add a new Subview to this UIViewController. The new Subview extended also of UIViewController. I added de view (Gray Rect) of the controller to the UIViewController but it is placed behind the NavigationBar. I don't want that.. So I searched for a solution and found something interesting..:
When I just add a UIView() (Green Rect) to the UIViewController, the placing of the view works perfectly, as I would love see it from the other UIViewController-View.
My code looks like following:
class DashboardController: UIViewController {
var ccview:ContactCircleController!
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = []
self.view.backgroundColor = .white
let test = UIView()
test.backgroundColor = .green
test.frame = CGRect(x: self.view.frame.width - 200, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
self.view.addSubview(test)
setup()
}
func setup(){
ccview = ContactCircleController()
ccview.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
ccview.edgesForExtendedLayout = UIRectEdge.top
self.view.addSubview(ccview.view)
}}
I've already unchecked the "Extend Edges" - toggle of the navigationcontroller on the storyboard. I also added edgesForExtendedLayout = [] to the UIViewController and for the UIView it worked fine. But for the view of another UIViewController... it didn't worked.
Thank you!
If you use Debug View Hierarchy, you will see that your gray ccview.view is not behind the navigation bar, but rather it is not keeping the height of self.view.frame.width / 2.
This is because the .view of a UIViewController instantiated from Storyboard has by default an .autoresizingMask = [], whereas the .view of a UIViewController instantiated without a storyboard has a default .autoresizingMask = [.flexibleWidth, .flexibleHeight].
You can correct this by changing setup() to:
func setup(){
ccview = ContactCircleController()
ccview.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
// remove this line
//ccview.edgesForExtendedLayout = UIRectEdge.top
// add this line
ccview.view.autoresizingMask = []
self.view.addSubview(ccview.view)
}

childViewController.view.frame in UIView present different value

I use childViewController to separate view in project, but some strange issue happened, here is my code.
class ViewController: UIViewController {
var container = UIView()
var childVC = ChildViewController()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
addChildViewController(childVC)
childVC.didMove(toParentViewController: self)
addChildView()
setContainerFrame()
}
func setContainerFrame() {
container.frame = CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 100)
container.backgroundColor = .red
view.addSubview(container)
}
func addChildView() {
let frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
childVC.view.frame = frame
childVC.view.backgroundColor = .green
container.addSubview(childVC.view)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("childVC.view.frame: \(childVC.view.frame)")
}
}
class ChildViewController: UIViewController {
}
when I exchange the order invoking func addChildView() and func setContainerFrame() in ViewController's viewDidLoad(), xcode'console prints different frame log.
This is due to the autoresizingMask of the childVC.view. By default this is set to have flexible width and height. So if you set the frame of the container and then the frame of the childVC.view then you get the actual value you set but if you reverse it and set the frame of the childVC.view and then the container the frame of the childVC.view is automatically updated to have the same relative width and height.
For example if the frame size for the container was 100 x 100 and then you change it to 200 x 200 the frame size for childVC.view will be doubled.
To remove this set the childVC.view.autoresizingMask = [] so it remains as you set it.
The strange behavior occurs because in addChildView() function you are adding the child view controller's view as subview to container view but container view's frame is setting in the function setContainerFrame() which is not yet called. That means you are adding a subview to a view whose frame hasn't set already.

Add UIView over Navigation Bar after Push Segue

I have two ViewControllers. I want to add an UIView over the navigation bar.
I could add the UIView on top of the first navigation bar by creating an UINavigationItem Outlet and adding the UIView created programatically to the UINavigationItem outlet titleView.
Code Snippet (HomeVC - Fist ViewController):
class HomeVC: UIViewController {
#IBOutlet weak var homeNavigationItem: UINavigationItem!
let navBarView = UIView()
let topNavBarLabel = UILabel()
let screenWidth = UIScreen.main.bounds.width
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navBarView.frame = CGRect(x: 0.0, y: 0.0, width: screenWidth, height: 50.0)
topNavBarLabel.text = "Hello World"
topNavBarLabel.textAlignment = NSTextAlignment.center
topNavBarLabel.textColor = UIColor.white
topNavBarLabel.frame = CGRect(x: 0.0, y: 10.0, width: screenWidth, height: 20.0)
navBarView.addSubview(topNavBarLabel)
homeNavigationItem.titleView = navBarView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Screenshot Home View:
Now incase of the SecondVC I am trying to do the same thing after the Push Segue.
Code Snippet (DetailVC - Second ViewController):
class DetailsVC: UIViewController {
let navBarView = UIView()
let screenWidth = UIScreen.main.bounds.width
let navBarTopLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navBarView.frame = CGRect(x: 0.0, y: 50.0, width: screenWidth, height: 50.0)
navBarView.backgroundColor = UIColor.black
navBarTopLabel.frame = CGRect(x: 0.0, y: 10.0, width: screenWidth, height: 20.0)
navBarTopLabel.textColor = UIColor.white
navBarTopLabel.text = "Details Hello"
navBarTopLabel.textAlignment = NSTextAlignment.center
navBarView.addSubview(navBarTopLabel)
self.view.addSubview(navBarView)
self.view.bringSubview(toFront: navBarView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Screenshot Details View:
Storyboard Screenshot:
View Hierarchy:
Note: I deliberately assigned an Y-position to the view more than 0, to be sure that the view is being created
But as I am not able to create an UINavigationItem, I am adding the programmatically created view to the view and even trying to bring the subView to the front.
Note: Yes, the size of the UINavigationBar has been increased, please refer here: Change width/height UINavigationBar embedded in a Navigation Controller
You could use the variable defined for UIViewController
public var navigationItem: UINavigationItem { get }
From the docs:
Created on-demand so that a view controller may customize its navigation appearance.
So in each subclass of UIViewController in viewDidLoad you can just do
self.navigationItem.titleView = navBarView

Pull to Refresh plug-in : PullToBounce Wrapper UIScrollView

I am trying to use this plugin as refresh action : https://github.com/entotsu/PullToBounce
One, issue is I can't understand his explanation.
Explanation given on the github
tableView.frame = yourFrame --> tableView is equal to scrollView.frame in my situation
yourFrame --> I have no idea what it is. The main frame ? Another Frame I have to create ?
bodyView.addSubview(tableViewWrapper) --> bodyView ? Main Frame here ? or Another frame ?
Here is my code for the scrollView for now. Any help on how to implement this plugin using a scrollView made via the storyboard.
class ProfileViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
func makeMock() {
let headerView = UIView()
headerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 64)
headerView.backgroundColor = UIColor.lightBlue
self.view.addSubview(headerView)
let headerLine = UIView()
headerLine.frame = CGRect(x: 0, y: 0, width: 120, height: 8)
headerLine.layer.cornerRadius = headerLine.frame.height/2
headerLine.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.8)
headerLine.center = CGPoint(x: headerView.frame.center.x, y: 20 + 44/2)
headerView.addSubview(headerLine)
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue
let bodyView = UIView()
bodyView.frame = scrollView.frame
bodyView.frame.y += 20 + 44
self.view.addSubview(bodyView)
let tableViewWrapper = PullToBounceWrapper(scrollView: scrollView)
bodyView.addSubview(tableViewWrapper)
tableViewWrapper.didPullToRefresh = {
NSTimer.schedule(delay: 2) { timer in
tableViewWrapper.stopLoadingAnimation()
}
}
makeMock()
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
One thing, I notice is that there is a View on top of my scrollView that disable me to view it and scroll it. Help here needed please.
Regards,
Hary
Take a look at the Example of this library.
yourFrame is nothing but your tableview class. For example if your tableView Class is named SampleTableView, then it goes like
let tableView = SampleTableView(frame: self.view.frame, style: UITableViewStyle.Plain).
You have to use another class to set up your tableView.

Resources