I'm making a very simple app for a demo and am trying to present a webpage using SFSafariViewController (I need to use SF versus WKWebView so to be able to access cookies).
I would really like to present the User with some UI buttons, but I've been unable to pull it off.
I tried this snippet (placed in the completion callback of presentViewController():
let width: CGFloat = 66
let x: CGFloat = self.view.frame.width - width
// It can be any overlay. May be your logo image here inside an imageView.
let overlay = UIView(frame: CGRect(x: x, y: 20, width: width, height: 44))
overlay.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
svc.view.addSubview(overlay)
... outlined in this post. In their case, they're attempting to cover the reload button with a small view. Regardless of the use-case, for me the view immediately disappears when I load SFSafariViewController (I can see it for a moment and it disappears).
I was thinking about presenting the button in an .OverContext modal, but then the User would be unable to interact with the SFSafariViewController, which also doesn't work.
Here's essentially what I'm after (pardon the gross, quick mockup) ... basically, SafariViewController with a view presented over it (see bottom) ... the transparency is just to show that it's being presented over Safari).
Any recommendations are greatly appreciated.
Figured it out ... there's likely some slight race condition going on that was preventing the recommended "draw a rectangle" code from working as desired. What I did:
Subclassed SFSafariWebViewController
In viewDidAppear, implemented a slight delay using NSTimer that draws any additional view elements
This also ended up helping me hide the status bar, which was giving me issues (see mockup).
Here's the relevant code:
import UIKit
import SafariServices
class MySafariVC: SFSafariViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var frame = self.view.frame
let OffsetY: CGFloat = 44
frame.origin = CGPoint(x: frame.origin.x, y: frame.origin.y - OffsetY)
frame.size = CGSize(width: frame.width, height: frame.height + (1 * OffsetY))
self.view.frame = frame
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "drawNavBar", userInfo: nil, repeats: false)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("i laid out my subviews")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func drawNavBar() {
let height: CGFloat = 44
let y: CGFloat = self.view.frame.height - height
// It can be any overlay. May be your logo image here inside an imageView.
let overlay = UIView(frame: CGRect(x: 0, y: y, width: self.view.frame.width, height: height))
overlay.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.9)
self.view.addSubview(overlay)
}
}
Related
I am using storyboard for UI builder, and I added this in viewDidLoad. But the bottom border doesn't go all the way the "EnteredEmail.frame.width" equals the size of the textfield in the storyboard which is iPhone SE
override func viewDidLoad() {
super.viewDidLoad()
let EmailBottomLine = CALayer()
EmailBottomLine.frame = CGRect(x: 0.0, y: EnteredEmail.frame.height-5, width: EnteredEmail.frame.width, height: 5)
EmailBottomLine.backgroundColor = UIColor.black.cgColor
EnteredEmail.layer.addSublayer(EmailBottomLine)
}
Simply you need to move your code to viewDidAppear() instead of viewWilllayoutSubviews().
viewDidAppear(_ animated: Bool)
This is called when the screen is shown to the user. This is a good place to put code where you might need to get the position of elements on the screen and run animations.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let EmailBottomLine = CALayer()
EmailBottomLine.frame = CGRect(x: 0.0, y: EnteredEmail.frame.height-5, width: EnteredEmail.frame.width, height: 5)
EmailBottomLine.backgroundColor = UIColor.black.cgColor
EnteredEmail.layer.addSublayer(EmailBottomLine)
}
I'm studying iOS' UIView!
And I found that I can't understand how bounds works.
For example,
Please run this code ... and see red box's moving.
The red box goes up! and white root view is static!
Why!? why red box goes up! ?? please let me know OTL!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView : UIView = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 200) )
childView.backgroundColor = UIColor.red
self.view.addSubview(childView)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 8, animations: {
// I cannot unnerstand how bounds works!!
self.view.bounds = CGRect(x: 0, y: -300, width:self.view.bounds.width, height: 700)
//I can understand how frame works
self.view.frame = CGRect(x: 200, y: 0, width: self.view.bounds.width, height: self.view.frame.height)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I read this article, but I can not understand
CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x;
CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y;
HERE! this is right ( according to what I tested, it was right. ) but, I don't know why. Why not "View.frame.origin.x + Superview.bounds.origin.x" ?
If we change the origin of view's bounds and the view moves on screen , what is the difference between "bounds" and "frame" in perspective of origin(or position) ? Is there a difference other than simply acting in reverse?
Basically, The bounds of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to its own coordinate system (0,0).
The frame of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to the superview it is contained within.
Please follow this link for clear example. Cocoa: What's the difference between the frame and the bounds?
This question already has an answer here:
Why iOS coordinate system is difficult to understand?? only me?
(1 answer)
Closed 5 years ago.
nice to meet you. I am studying "Coordinate System" of UIView of iOS.
Frame is easy to understand, but Bounds isn't.
For example, Frame works as expected when you change origin.
Bounds does not change its position even if we change origin.
import UIKit
class ViewController: UIViewController {
var childView : UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let rect = CGRect(x: 20, y: 30, width: 200, height: 200)
childView = UIView(frame: rect)
childView.backgroundColor = UIColor.red
self.view.addSubview(childView)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 5, animations: {
self.childView.bounds = CGRect(x: 100, y: 150, width: 200, height: 200)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
"Why do we need Bounds?" I thought.
And I searched hard on the internet. So, there are some facts I found out.
----First----
Bounds literally means borderline. iOS can draw a picture only within that boundary line. As you can see in the figure above, the UIButton also has Bounds, and when it exceeds the Bounds, the picture is cut and drawn.
---- Second ----
Size of Bounds and Size of Frame are the same.
It's not important, but I tried to test it out.
---- Thirds ----
bounds.origin is also useful in View Hierarchy. But it works differently from Frame.
Frame is easy to understand.
The ViewController's RootView will be 700 from top to button.
import UIKit
class ViewController: UIViewController {
var childView : UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let rect = CGRect(x: 100, y: 100, width: 200, height: 200)
childView = UIView(frame: rect)
childView.backgroundColor = UIColor.red
self.view.addSubview(childView)
self.view.backgroundColor = UIColor.cyan
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 10, animations: {
self.view.frame = CGRect(x: 0, y: 700, width: self.view.frame.size.width, height: self.view.frame.size.height)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If you look at the Coordinate System in iOS, you can see that Frame should work like that.
However, Bounds does not move itself, but only the Subview below it. I do not understand this part.
import UIKit
class ViewController: UIViewController {
var childView : UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let rect = CGRect(x: 100, y: 100, width: 200, height: 200)
childView = UIView(frame: rect)
childView.backgroundColor = UIColor.red
self.view.addSubview(childView)
self.view.backgroundColor = UIColor.cyan
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 10, animations: {
self.view.bounds = CGRect(x: 0, y: 700, width: self.view.frame.size.width, height: self.view.frame.size.height)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
So, I searched it in google.
Eventually, I found a mathematical formula for "SubView" to reposition when SuperView.origin.bounds was changed.
( 출처 : article )
CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x;
CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y;
By the way, why did Apple use these formulas? Basically, based on the "Coordinate System" we think,
CompositedPosition.x = View.frame.origin.x + Superview.bounds.origin.x;
CompositedPosition.y = View.frame.origin.y + Superview.bounds.origin.y;
Is not this formula more intuitive?
So, My Question are these.
When I increase SuperView.bounds.origin.y by 700 in its original state, why does SubView move up not moving down ???
Should I just accept the formula and memorize it?
---------------------------------------------------------------
----------------------------Edit--1----------------------------
Now I Got the concept of bounds by studying UIScrollView!
CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x;
CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y;
This is right.
This is a picture what I understand.
yes, I agree this picture looks dizzy sorry!
If I scroll UP the scrollview, and bounds.origin.y will increase and offset.y will increase and the subviews attached in scrollview's frame.origin.y will not change but, iOS calculate where the subviews to draw(CompositedPosition) by using that formula so! It looks like that the subviews goes UP!.
In brief, bounds change -> iOS calculate by using that formula -> draw!
First, please post in English or another supported language.
Second I think I might have understand what you are going for and here is the general simple answer.
First there is a coordinate system for everything, but each view also has its own coordinated system. Each subview of a view is defined inside of the superviews coordinate system.
I would try and say more but I am purely going of the pictures. And also even in the post was in English, I have trouble believing this post is a minimal example of the presented problem
I'm trying to add a little bit of extra height to the content of a UIScrollView that is within a WKWebView after it loads by adjusting the contentSize property.
I can modify this property, but it somehow keeps changing back to its original size by the time the next layout/display refresh hits.
To test this even further, I attempted to change contentSize in scrollViewDidScroll. Whenever you scroll to the bottom, you can see for a fraction of a second that it's trying add the extra space and keeps reverting back.
I can't reproduce this issue with UIWebView. It works just fine there. Perhaps some changes were added to WKWebView recently? I'm using Xcode 8 and testing on iOS 9/10.
Given my ineptitude with Dropbox I felt badly so put the attached together to try and help you out. If you change the contentInset property of the WKWebView's scrollView rather than contentSize, this seems to work quite well. I agree with you that while you might be able temporarily to change the content size of the scrollView, it reverts quickly; moreover, there are no delegate methods either for UIScrollView or WKWebView that I can find that you might override to counteract this.
The following sample code has a web page and some buttons that allow you to increase or decrease the top and bottom contentInset and animating you to the appropriate point on the scrollView.
import UIKit
import WebKit
class ViewController: UIViewController {
var webView : WKWebView!
var upButton : UIButton!
var downButton : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let webFrame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: self.view.frame.width - 200, height: self.view.frame.height - 200))
webView = WKWebView(frame: webFrame)
webView.load(URLRequest(url: URL(string: <PUT RELEVANT URL STRING (NB - THAT YOU ARE SURE IS VALID) HERE>)!))
webView.backgroundColor = UIColor.red
webView.scrollView.contentMode = .scaleToFill
self.view.addSubview(webView)
func getButton(_ label: String) -> UIButton {
let b : UIButton = UIButton()
b.setTitle(label, for: .normal)
b.setTitleColor(UIColor.black, for: .normal)
b.layer.borderColor = UIColor.black.cgColor
b.layer.borderWidth = 1.0
return b
}
let upButton = getButton("Up")
let downButton = getButton("Down")
upButton.frame = CGRect(origin: CGPoint(x: 25, y: 25), size: CGSize(width: 50, height: 50))
downButton.frame = CGRect(origin: CGPoint(x: 25, y: 100), size: CGSize(width: 50, height: 50))
upButton.addTarget(self, action: #selector(increaseContentInset), for: .touchUpInside)
downButton.addTarget(self, action: #selector(decreaseContentInset), for: .touchUpInside)
self.view.addSubview(webView)
self.view.addSubview(upButton)
self.view.addSubview(downButton)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func increaseContentInset() -> Void {
guard let _ = webView else { return }
webView.scrollView.contentInset = UIEdgeInsetsMake(webView.scrollView.contentInset.top + 100, 0, webView.scrollView.contentInset.bottom + 100, 0)
webView.scrollView.setContentOffset(CGPoint(x: webView.scrollView.contentInset.left, y: -1 * webView.scrollView.contentInset.top), animated: true)
}
func decreaseContentInset() -> Void {
guard let _ = webView else { return }
webView.scrollView.contentInset = UIEdgeInsetsMake(webView.scrollView.contentInset.top - 100, 0, webView.scrollView.contentInset.bottom - 100, 0)
webView.scrollView.setContentOffset(CGPoint(x: webView.scrollView.contentInset.left, y: -1 * webView.scrollView.contentInset.top), animated: true)
}
}
I hope that helps. If you need an answer based specifically on setting the content size then let me know, but I think this is the best option.
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.