I have a web view that display a table much larger than the screen. When I try to scroll it diagonally AFTER zooming - both in or out, it usually only scrolls in a single direction instead of diagonally. Is this behaviour due to web view's scrollview or could I have made mistake in my codes?
This is how I populate my web view:
webViewContent.scrollView.bounces = false
webViewContent.scrollView.bouncesZoom = false
webViewContent.scrollView.delegate = self
webViewContent.scalesPageToFit = true
var htmlString = "<html><head>... ... a really long string that creates a table"
webViewContent.loadHTMLString(htmlString, baseURL: nil)
Please do tell me if the htmlString might affect, I did not include it because it is really long.
I also tried to synchronise the view with a header row called webViewTitle which I populate using similar codes but only one row. The synchronise codes are like:
func scrollViewDidZoom(_ scrollView: UIScrollView) {
if(scrollView != webViewTitle.scrollView){
var zoomPadding:CGFloat = 0.0
if((currentZoomScale*webViewContent.scrollView.zoomScale) < 1){
zoomPadding = 0.5*(-acos(currentZoomScale*webViewContent.scrollView.zoomScale)*180.0/CGFloat.pi)
}else{
zoomPadding = 0.5*acos(2-(currentZoomScale*webViewContent.scrollView.zoomScale))*180.0/CGFloat.pi
}
webViewTitle.scrollView.zoom(to: CGRect(x: webViewContent.scrollView.contentOffset.x,
y: (355*currentZoomScale*webViewContent.scrollView.zoomScale) + zoomPadding,
width: webViewTitle.scrollView.bounds.width/currentZoomScale/webViewContent.scrollView.zoomScale,
height: webViewTitle.scrollView.bounds.height/currentZoomScale/webViewContent.scrollView.zoomScale),
animated: false)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var zoomPadding:CGFloat = 0.0
if((currentZoomScale*webViewContent.scrollView.zoomScale) < 1){
zoomPadding = 0.5*(-acos(currentZoomScale*webViewContent.scrollView.zoomScale)*180.0/CGFloat.pi)
}else{
zoomPadding = 0.5*acos(2-(currentZoomScale*webViewContent.scrollView.zoomScale))*180.0/CGFloat.pi
}
if(scrollView == webViewTitle.scrollView){
webViewTitle.scrollView.contentOffset.y = (355*currentZoomScale*webViewContent.scrollView.zoomScale) + zoomPadding
webViewContent.scrollView.contentOffset.x = webViewTitle.scrollView.contentOffset.x
}else{
webViewTitle.scrollView.contentOffset.y = (355*currentZoomScale*webViewContent.scrollView.zoomScale) + zoomPadding
webViewTitle.scrollView.contentOffset.x = webViewContent.scrollView.contentOffset.x
}
}
Could any of these caused the diagonal scrolling to become buggy?
Could any of these caused the diagonal scrolling to become buggy?
Yes
From the provided code, you are implementing the UIScrollViewDelegate for the UIWebView, but in both shown implementations, you are not calling super. This means that the scrollView is not going to have it's standard behavior, and instead strictly use your code for it's scrollViewDidScroll and scrollViewDidZoom behavior.
While this question doesn't necessarily provide a lot of clarity on exactly what is going on, I believe it to be the same issue you're facing.
In summary: At a minimum, call super for each delegate implementation of the UIScrollViewDelegate, or if you don't need any custom scroll behavior then remove the delegate altogether.
Related
INTRUDUCTION
I want to create a simple game where you should be able to drag a label and if you drag it in the correct place, you win. To be more specific: This is a game to help children with autism. In this game they have to create the correct sequence of numbers from one to ten dragging the label with the number in the correct place (which is an image actually). now you will understand better:
PROBLEM
I have already created the code to drag the labels (with a pan gesture recognizer) but I don't know how to create a Collision detection: When the card "1" is dragged and it collides with the blue image "1" something happens,
my mentally code is:
if LB_1 *collides with* IMG_1 {
self.IMG_1?.backgroundColor = UIColor.green
}
I hope the question it's clear.
Ah I don't use SpriteKit. I used "SingleViewApplication" as Template not "Game".
You want to use contains(). If you use intersects() then it will return true as soon as the two rectangles touch. With contains(), it won't return true until the dragged view is fully inside the target view, which seems much more intuitive.
I just wrote a sample app that implements this and it works perfectly.
I created a simple subclass of UIView I called BoxedView that just sets a border around it's layer so you can see it.
I set up a view controller with 2 boxed views, a larger "targetView", and a smaller view that the user could drag.
The target/action for my gesture recognizer moves the dragged view's frame as the user drags, and if target view contains the dragged view, it sets a Bool highlightTargetView, which causes the box around the target view to get thicker.
The entire view controller class' code looks like this:
class ViewController: UIViewController {
#IBOutlet weak var targetView: BoxedView!
var viewStartingFrame: CGRect = CGRect.zero
var highlightTargetView: Bool = false {
didSet {
targetView.layer.borderWidth = highlightTargetView ? 5 : 1
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func userDraggedView(_ gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .began:
viewStartingFrame = gesture.view?.frame ?? CGRect.zero
case .changed:
let offset = gesture.translation(in: view)
gesture.view?.frame = viewStartingFrame.offsetBy(dx: offset.x, dy: offset.y)
highlightTargetView = targetView.frame.contains(gesture.view?.frame ?? CGRect.zero)
case .ended:
gesture.view?.frame = viewStartingFrame
highlightTargetView = false
default:
break
}
}
}
In order for the math to work, I use the frame property of both views, which is in the coordinate system of the parent view (The parent view is the view controller's content view in this case, but the key thing is that we compare 2 rectangles using the same coordinate system for both.) If you used the bounds property of either view your math wouldn't work because bounds of a view is in the local coordinate system of that view.
Here's what that program looks like when running:
For comparison, I modified the program to also show what it looks like using the intersects() function, and created a video of what that looks like:
You can check if their frames intersect.
CGRect has method intersects.
So you're if statement should be the following:
if LB_1.frame.intersects(IMG_1.frame) {
self.IMG_1?.backgroundColor = UIColor.green
}
If that's not enough for you, you can calculate area of intersection rect.
I have a strange problem. I have a custom UIView that is supposed to fill the screen. Here is a picture of the GUI along with the constraints:
Now the main problem is, on the iPad Pro 12.9" simulator, at first the custom view only fills a portion of the screen- like it was following the Air 2 size constraints. However, if I go away from the screen and come back to it such that the screen isn't recreated but just redisplayed, the gui looks almost perfect. On the other hand, the gui looks almost perfect on the iPad Mini device that I have, without having to go and come back. It isn't quite there because the image in the middle section gets clipped slightly at the top and bottom, but I haven't tried hard to figure out why that is happening. I have spent a fair amount of time trying to debug the problem I am asking about. If you need more information to help me solve this problem, I'm happy to provide it- just specify what you need. On the view controllers that actually hold this custom view, I use autoresizing masks to have it fill the screen, which apparently isn't working, but constraints have been tried and they didn't help either.
Any ideas on how to fix this?
UPDATE: I changed the constraints to something I liked better, as I had used "Reset to Suggested Constraints" and that created some weird constraints. Problem still exists, however.
Here is some of the code involving the view:
class SessionDisplayViewController: SessionViewDisplayViewControllerBase
{
//some code omitted for succinctness
#IBOutlet weak var mySessionView: SessionDisplayView!
override func getSessionView() -> SessionDisplayView
{
return mySessionView
}
...
}
class SessionViewDisplayViewControllerBase: UIViewController, SessionDisplayViewDelegate{
...
override func viewDidLoad() {
super.viewDidLoad()
...
if !ShareData.sharedInstance.sessionDataObjectContainer.keys.contains(curSessName) || ShareData.sharedInstance.sessionDataObjectContainer[curSessName] == nil
{
setupMySession(isLive: false, isFinalized: false)
}
else if (ShareData.sharedInstance.sessionDataObjectContainer[curSessName]?.isFinalized)!
{
setupMySession(isLive: false, isFinalized: true)
}
else
{
setupMySession(isLive: true, isFinalized: false)
var fromTempChoose = ShareData.sharedInstance.startingSessionFromTempChoose && !(ShareData.sharedInstance.globalsVar?.hasStartedSession)!
if fromTempChoose || (ShareData.sharedInstance.resumingSessionFromSessDet && !(ShareData.sharedInstance.globalsVar?.hasResumedSession)!)
{
let mySessionView = getSessionView()
mySessionView.curScene.pauseSession(isStartingNow: true)
blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
//}
blurEffectView = UIVisualEffectView(effect: blurEffect)
//always fill the view
blurEffectView?.frame = self.view.bounds
blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(blurEffectView!)
}
...
}
var mySessObj = getSessionView() //these three lines of code were added to try to fix the problem. They weren't in the original code
mySessObj.frame = self.view.bounds
setNeedsDisplay()
}
...
func setupMySession(isLive: Bool, isFinalized: Bool)
{
let mySessionView = getSessionView()
//mySessionView.translatesAutoresizingMaskIntoConstraints = false
mySessionView.delegate = self
sessionNameIndex = self.getSessionNumber() - 1
let myName = ShareData.sharedInstance.currentAccount.name
var curSessName = generateCurrentAccountName(name: myName!, value: self.getSessionNumber())
//var names = generateAllPossibleSessionNames(name: myName!)
let curSession = ShareData.sharedInstance.sessionDataObjectContainer[curSessName]
mySessionView.onView(index: getSessionNumber(), sessionName: curSessName, isLive: isLive, isFinalized: isFinalized)
if isLive
{
let val = curSession?.currentValue()
mySessionView.curScene.setStartPosition(newValue: val!)
}
}
It took me awhile to figure this one out. The software was adding an auto resizing mask constraint to the view, which was causing it to look funny. In order to fix this problem, in the initialization of the custom UIView, I had to add the following lines of code:
self.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.view.frame = self.bounds
I guess the software treats the class that is the custom UIView and the view inside it that holds everything as two separate views. I wish the tutorials I found online about creating a custom UIView had included that we need to set the auto resizing mask and the view's frame, but oh well. Lesson learned.
i'm trying to add a sublayer behind the imageView however the issue is that since it is using constraints it can't seem to figure out the position and just places sublayer in left corner? i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
viewDidLoad
//GroupProfile ImageView
imageGroupProfile = UIImageView(frame: CGRect.zero)
imageGroupProfile.backgroundColor = UIColor.white
imageGroupProfile.clipsToBounds = true
imageGroupProfile.layer.cornerRadius = 50
self.view.addSubview(imageGroupProfile)
imageGroupProfile.snp.makeConstraints { (make) -> Void in
make.height.equalTo(100)
make.width.equalTo(100)
make.centerX.equalTo(self.view.snp.centerX)
make.centerY.equalTo(self.view.snp.centerY).offset(-40)
}
let pulseEffect = LFTPulseAnimation(repeatCount: Float.infinity, radius:160, position:imageGroupProfile.center)
self.view.layer.insertSublayer(pulseEffect, below: imageGroupProfile.layer)
i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
Nevertheless that is the way to do it. Just add a Bool property so that your implementation of viewDidLayoutSubViews inserts the layer only once:
var didLayout = false
override func viewDidLayoutSubviews() {
if !didLayout {
didLayout = true
// lay out that layer here
}
}
The reason is that you don't have the needed dimensions until after viewDidLayoutSubviews tells you that (wait for it) your view has been laid out! But, as you rightly say, it can be called many times subsequently, so you also add the condition so that your code runs just once, namely the first time viewDidLayoutSubviews is called.
I have this table view which in some cases contains few rows, even one.
What’s wrong with it is that even if I have one row, it allows me to scroll down and the row gets hidden at top.
It practically almost disappears from screen, as if there would be somethingto show below it.
I can’t disable scrolling because I have pull down to refresh.
Any ideas if there is a setting I am missing? Or how I could not allow scroll down if I do not have enough rows to cover the whole screen?
Actually, your case is kind of tricky, because:
The first I thought that the solution will be myTableView.alwaysBounceVertical = false
That's will do the job for you, but the problem in your case that you have a UIRefreshControl() and setting alwaysBounceVertical to false will disable scrolling to top for displaying the refreshController.
So, it should be done manually, as follows:
1- Implement the scrollViewDidScroll method from UIScrollViewDelegate.
2- check the scrolling direction in it.
3- if the scrolling direction goes down, check if content size of the tableView is more than its height, i.e check if tableView contains cell more than its height.
4- if the output of step 3 is false, disable scrolling, else, enable scrolling.
5- add dispatch_after to re-enable tableView scrolling.
It goes like this (Note: Swift 2 code.):
private var lastContentOffset: CGFloat = 0
// 1
func scrollViewDidScroll(scrollView: UIScrollView) {
// 2
if (self.lastContentOffset > scrollView.contentOffset.y) {
print("scrolling up")
}
else if (self.lastContentOffset < scrollView.contentOffset.y) {
print("scrolling down")
// 3 and 4
myTableView.scrollEnabled = myTableView.contentSize.height > myTableView.frame.size.height ? true : false
// 5
// delaying is half a second
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_MSEC) * 500), dispatch_get_main_queue(), { () -> Void in
self.myTableView.scrollEnabled = true
})
}
}
Hope this is a good solution for your case.
In my app I'm trying to implement custom table view scrolling. I'm trying to achieve something similar to Volvo ocean race app, which does it very well.
Basically when user scrolls, middle cell gets higher and current high cell gets smaller
My original idea was playing with cell's frame in scrollViewDidScroll method for example
func scrollViewDidScroll(scrollView: UIScrollView) {
if(self.tblViewPhotos.contentOffset.y > 0)
{
var thisCell = self.tblViewPhotos.cellForRowAtIndexPath(selectedIndexPath) as PhotoCell
var offset = self.tblViewPhotos.contentOffset.y;
thisCell.frame = CGRectMake(thisCell.frame.origin.x, thisCell.frame.origin.y, thisCell.frame.size.width, 250-offset);
}
}
however I'm getting very buggy results (frame changing suddenly when swiping fast etc). I would be glad, if someone can point me to right direction