Change swipe to delete button height in UITableview in Xamarin IOS - xamarin.ios

I have a table view with cells having some padding around them. I implemented swipe to delete feature and by default the delete button occupies the cell height.
I used below code for IOS 10 to align it with the visible cell height and it is screwing up the button height in a wierd way. Note : I have got it working fine in IOS 11 using a different set of code as the way to handle this is different between IOS 10 and IOS 11.
But below code for IOS 10 screws up button height. Looks like the layout subview gets called multiple times when user swipes and that is causing the button height to vary a lot. Any ideas of how to solve this.
public override void LayoutSubviews()
{
base.LayoutSubviews();
if (Convert.ToInt16(UIDevice.CurrentDevice.SystemVersion.Split('.')[0]) < 11)
{
foreach (var view in this.Subviews)
{
if (view.Class.Name.ToString() == "UITableViewCellDeleteConfirmationView")
{
CGRect newFrame = view.Frame;
newFrame.Y = newFrame.Y + 6;
newFrame.Height = newFrame.Height - 12;
view.Frame = newFrame;
}
}
}
}

Refer to this post.
The view hierarchy before iOS11 demonstrates as below
UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView -> _UITableViewCellActionButton
I see you get the UITableViewCellDeleteConfirmationView not the Button .
Modify your code :
foreach (UIView subview in this.Subviews)
{
if (subview.Class.Name.ToString() == "UITableViewCellDeleteConfirmationView")
{
foreach (var view in subview.Subviews)
{
if (view.Class.Name.ToString() == "_UITableViewCellActionButton")
{
CGRect newFrame = view.Frame;
newFrame.Y = newFrame.Y + 6;
newFrame.Height = newFrame.Height - 12;
view.Frame = newFrame;
}
}
}
}

Related

How do I change the background color of a tabbed page in Xamarin iOS?

I need to change the background color of the currently tabbed page in my UITabBarController. I've searched through every stackoverflow post I could find but nothing worked for me. I thought there would be something like UITabBar.Appearance.SelectedImageTintColor, just for the background color but it doesn't seem so.
For example, I want to change the color of that part when I am on the right tab:
Does someone know how to do that?
You could invoked the following code in your UITabBarController
public xxxTabBarController()
{
//...set ViewControllers
this.TabBar.BarTintColor = UIColor.Red;
}
Update
//3.0 here is if you have three child page in tab , set it as the current value in your project
//
var size = new CGSize(TabBar.Frame.Width / 3.0, IsFullScreen());
this.TabBar.SelectionIndicatorImage = ImageWithColor(size,UIColor.Green);
double IsFullScreen()
{
double height = 64;
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
if (UIApplication.SharedApplication.Delegate.GetWindow().SafeAreaInsets.Bottom > 0.0)
{
height = 84;
}
}
return height;
}
UIImage ImageWithColor(CGSize size, UIColor color)
{
var rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContextWithOptions(size, false, 0);
CGContext context = UIGraphics.GetCurrentContext();
context.SetFillColor(color.CGColor);
context.FillRect(rect);
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
The trick is to use the SelectionIndicatorImage Property of the UITabBar and generate a completely filled image with your desired color using the following method:
private UIImage ImageWithColor(CGSize size)
{
CGRect rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContext(size);
using (CGContext context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(UIColor.Green); //change color if necessary
context.FillRect(rect);
}
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
To initialize everything we override ViewWillLayoutSubviews() like this:
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
// The tabbar height will always be 49 unless we force it to reevaluate it's size on runtime ...
myTabBar.InvalidateIntrinsicContentSize();
double height = myTabBar.Frame.Height;
CGSize size = new CGSize(new nfloat(myTabBar.Frame.Width / myTabBar.Items.Length, height));
// Now get our all-green image...
UIImage image = ImageWithColor(size);
// And set it as the selection indicator
myTabBar.SelectionIndicatorImage = image;
}
As mentioned in this article (google translating it step by step when necessary lol) calling InvalidateIntrinsicContentSize() will force the UITabBar to reevaluate it's size and will get you the actual runtime height of the tab bar (instead of the constant 49 height value from XCode).

WKWebView not rendering correctly in iOS 10

I have WKWebView inside the UITableViewCell. The web view load request and then after finished loading, I'll resize the web view height to be equal to its content height, then adjust table view cell height to fit accordingly.
What happened was the web view only displays the area that fit the screen size. When I scroll down, everything is white. But I see that the web view rendered with correct height, tap and hold on the white area on the web view still see selection. Zooming with pinch makes the web view area that displaying on the screen visible, but other areas sometimes become white.
It works fine on iOS 8 and 9. I have created a sample project to demonstrate this behaviour here:
https://github.com/pawin/strange-wkwebview
Open Radar:
https://openradar.appspot.com/radar?id=4944718286815232
Update: This issue is resolved in iOS11
You need to force the WKWebView to layout while your UITableView scrolls.
// in the UITableViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
if let tableView = scrollView as? UITableView {
for cell in tableView.visibleCells {
guard let cell = cell as? MyCustomCellClass else { continue }
cell.webView?.setNeedsLayout()
}
}
}
func reloadWKWebViewIfNeeded() {
for cell in self.tableView.visibleCells {
guard let webviewCell = cell as? WebviewCell else { continue }
// guard cell height > screen height
webviewCell.webview.reload()
}
}
override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
guard !decelerate else { return }
self.reloadWKWebViewIfNeeded()
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
self.reloadWKWebViewIfNeeded()
}
Not the best solution though, but at least user can see the rest of the content
I have the same problem - add a WKWebView to a UITableViewCell, and I solved this problem by these steps:
1.Create a UITextView instance and add it to UITableView's superview(UIViewControllew.view)
2.implement codes in scrollViewDidScroll like this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self.xxtextView becomeFirstResponder];
[self.xxtextView resignFirstResponder];
}
These codes may cause increased cpu performance overhead, you can fix it by some way such us use a temp variable as threshold value.
I don't think this is a perfect solve method, but it works for me.
Eventually I realized that textview becomeFirstResponder just led the webview layout again, so you can just fix it like this:
CGFloat tempOffset = 0;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (!tempOffset || ABS(scrollView.contentOffset.y - tempOffset) > ScreenHeight/2)
{
[self.wkWebView setNeedsLayout];
tempOffset = scrollView.contentOffset.y;
}
}
In objective-C this is how I solve the problem.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSArray * visibleCell = [self.tableView visibleCells];
for (CustomUITableViewCell * cell in visibleCell) {
if ([cell isKindOfClass:[CustomUITableViewCell class]]) {
[cell.wkWebView setNeedsLayout];
}
}
}
That code will collect all visible cell and do the setNeedsLayout in fast enumeration during user scroll.
Im also have uitableview with cell with wkWebView. And i stack with same problem. But timely you can fix this with this code. By performance do not worry. I tested this solution on iphone 5s and it was taken 10-15% CPU only when you scrolling uitableView with visible web cell.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//TODO: Remove this fix when WKWebView will fixed
if let cell = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? WKWebViewCell {
// Here we take our cell
cell.wkWebView?.setNeedsLayout()
// here is "magic" (where wkWebView it is WKWebView, which was
// previously added on cell)
}
}

UIScrollView horizontally scrolling menu in Sprite Kit

I am trying to make a horizontally scrolling menu in my new Sprite Kit game. With Sprite Kit being fairly new, there aren't many good tutorials for Sprite Kit. Is UIScrollView compatible with Sprite Kit? I have tried a couple of ways such as this:
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.pagingEnabled = YES;
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5 alpha:1];
[scroll addSubview:awesomeView];
}
This didn't work. I have never worked with UIScrollView before. Can anyone give a way of making a horizontally scrolling menu using UIScrollView? An example of what I am trying to do is in Bloons TD 5 with their menu.
I know this question is a few months old already, but I was also looking for a scrolling menu slider to select levels in my SpriteKit game. I couldn't find any code already existing and the thought of force fitting the UIScrollView into my SpriteKit game didn't appeal to me. So I wrote my own sliding menu for level selection.
I've simulated all of the motions of the UIScrollView so it looks natural, and can be configured to scroll left - right or up - down.
As a bonus I created a parallax background on the menu which can be easily swapped out with new images for a custom look.
https://github.com/hsilived/ScrollingMenu
The code for the Scrolling menu is very well commented so there shouldn't be to many integration issues.
The github project has a fully working demo with Worlds, Levels and parallax images. If you like it mark my answer as useful so I can gain some reputation :)
You can definitely combine UIKit controls with SpriteKit. From the SKScene where you want to have a UIScrollView add the scroll view to self.view, like this: [self.view addSubview:scroll]. You may want to do this in the -(void)didMoveToView:(SKView*)view method on your SKScene. Remember to remove the scroll view from the SKView once you transition out of the SKScene that is using it. You can do that in the -(void)willMoveFromView:(SKView*)view method. Don't forget to set the contentSize on the UIScrollView.
Note: If you want to have SpriteKit nodes over the UIKit controls then you will need to write your own SKSpriteNode that behaves like a UIScrollView.
Expanding on Note: If your scroll view needs to contain Sprite-kit nodes and not just UIKit views you won't be able to use UIScrollView unless you do something clever as suggested in the answer to this question here
There are many ways to obtain it, through SKCameraNode, using UIKit elements like UIScrollView or other ways..
But I want to show you how to do it simple with SpriteKit only.
Conceptually, you should build a larger SKNode that containt every single voice of your menu where each element have an equal distance from the other.
You can simulate the scrolling with an SKAction that move our node to the center of the nearest visible element.
GameViewController:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = false
view.showsDrawCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
GameScene:
class GameScene: SKScene {
var lastX: CGFloat = 0.0
var moveableArea = SKNode() // the larger SKNode
var worlds: [String]! = ["world1","world2","world3"] // the menu voices
var currentWorld:Int = 0 // contain the current visible menu voice index
var worldsPos = [CGPoint]() // an array of CGPoints to store the menu voices positions
override func didMove(to view: SKView) {
//Make a generic title
let topLabel = SKLabelNode.init(text: "select world")
topLabel.fontSize = 40.0
self.addChild(topLabel)
topLabel.zPosition = 1
topLabel.position = CGPoint(x:frame.width / 2, y:frame.height*0.80)
// Prepare movable area
self.addChild(moveableArea)
moveableArea.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
// Prepare worlds:
for i in 0..<worlds.count {
// add a title for i^ world
let worldLabel = SKLabelNode.init(text: worlds[i])
self.moveableArea.addChild(worldLabel)
worldLabel.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:moveableArea.frame.height*0.60)
// add a sprite for i^ world
let randomRed = CGFloat(drand48())
let randomGreen = CGFloat(drand48())
let randomBlue = CGFloat(drand48())
let randomColor = UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
let worldSprite = SKSpriteNode.init(color: randomColor, size: CGSize.init(width: 100.0, height: 100.0))
worldSprite.name = worlds[i]
self.moveableArea.addChild(worldSprite)
worldSprite.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:0.0)
}
}
func tapNode(node:SKNode,action:SKAction) {
if node.action(forKey: "tap") == nil {
node.run(action, withKey: "tap")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let touchedNode = self.atPoint(location)
lastX = location.x
let sequence = SKAction.sequence([SKAction.scale(to: 1.5, duration: 0.2),SKAction.scale(to: 1.0, duration: 0.1)]) // a little action to show the selection of the world
switch touchedNode.name {
case "world1"?:
print("world1 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world2"?:
print("world2 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world3"?:
print("world3 was touched..")
tapNode(node:touchedNode,action:sequence)
default:break
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let currentX = location.x
let leftLimit:CGFloat = CGFloat(worlds.count-1)
let rightLimit:CGFloat = 1.0
let scrollSpeed:CGFloat = 1.0
let newX = moveableArea.position.x + ((currentX - lastX)*scrollSpeed)
if newX < self.size.width*(-leftLimit) {
moveableArea.position = CGPoint(x:self.size.width*(-leftLimit), y:moveableArea.position.y)
}
else if newX > self.size.width*rightLimit {
moveableArea.position = CGPoint(x:self.size.width*rightLimit, y:moveableArea.position.y)
}
else {
moveableArea.position = CGPoint(x:newX, y:moveableArea.position.y)
}
// detect current visible world
worldsPos = [CGPoint]()
for i in 0..<worlds.count {
let leftLimit = self.size.width-(self.size.width*CGFloat(i))
let rightLimit = self.size.width-(self.size.width*CGFloat(i+1))
if rightLimit ... leftLimit ~= moveableArea.position.x {
currentWorld = i
}
worldsPos.append(CGPoint(x: (rightLimit + (leftLimit - rightLimit)/2), y:moveableArea.position.y))
}
lastX = currentX
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if worldsPos.count>0, moveableArea.action(forKey: "moveAction") == nil {
let moveAction = SKAction.move(to: worldsPos[currentWorld], duration: 0.5)
moveAction.timingMode = .easeInEaseOut
self.moveableArea.run(moveAction, withKey: "moveAction")
}
}
}
As you can see , with touchesBegan we can detect the touched world and select it, with touchesMoved we are able to move the moveable node to the correct position and to detect the nearest visible world and with touchesEnded we can create the smooth scrolling to automatic move to the current nearest visible voice after a touch..
Output:

JavaFX ScrollPane showing objects outside of viewport

In my current application I have created a ScrollPane with an AnchorPane as content. Depending on the actions of the user this AnchorPane will be filled with more or less images for which I use a Canvas(necessary for drawing more information on the image).
However when I scroll through the ScrollPane, all the child images are still being repainted even when they aren't inside the ViewPort. Has anyone else had this problem or a solution for this?
screenshot:
https://dl.dropboxusercontent.com/u/51537629/images%20drawn%20outside%20of%20viewport.png
Initialization of the scrollpane:
iconPane = new AnchorPane();
iconPane.setStyle("-fx-background-color: white; -fx-border-color: gray;");
iconPane.setMinHeight(546);
iconPane.setMinWidth(814);
scrollpane = new ScrollPane();
scrollpane.setContent(iconPane);
scrollpane.setVisible(false);
AnchorPane.setTopAnchor(scrollpane, 13.0);
AnchorPane.setBottomAnchor(scrollpane, 13.0);
AnchorPane.setLeftAnchor(scrollpane, 13.0);
AnchorPane.setRightAnchor(scrollpane, 13.0);
Creation of an icon:
private VBox createIconPane(TaskInformation tinfo) {
VBox vbox = new VBox();
Canvas canvas = new Canvas(75, 75);
GraphicsContext gc = canvas.getGraphicsContext2D();
Image im;
if (tinfo.getCurrent() != null) {
if (tinfo.isCurrentStop()) {
String status = utilities.getCurrentTaskVersion(tinfo.getTask()).getStatus();
if (status.equals("finished")) {
im = new Image(this.getClass().getClassLoader().getResourceAsStream("dna-current.png"));
} else if (status.equals("failed")) {
im = new Image(this.getClass().getClassLoader().getResourceAsStream("dna-failed.png"));
} else {
im = new Image(this.getClass().getClassLoader().getResourceAsStream("dna-processing.png"));
}
} else {
im = new Image(this.getClass().getClassLoader().getResourceAsStream("dna-current.png"));
}
} else {
im = new Image(this.getClass().getClassLoader().getResourceAsStream("dna-excluded.png"));
}
gc.drawImage(im, 5, 5);
gc.setStroke(Color.GREEN);
gc.strokeText(tinfo.getFinished() + "", 59, 15);
gc.setStroke(Color.RED);
gc.strokeText(tinfo.getFailed() + "", 59, 28);
gc.setStroke(Color.BLUE);
gc.strokeText(tinfo.getProcessing() + "", 59, 41);
Label namelabel = new Label(tinfo.getTask().getName());
namelabel.setLayoutX(0);
namelabel.setLayoutY(68);
vbox.getChildren().addAll(canvas,
return vbox;
}
Addition of all icons to the view:
private void createChildIcon(TaskInformation tinfo) {
VBox taskicon = createIconPane(tinfo);
taskicon.setLayoutX((tinfo.getTask().getLevel() - 6) / 2 * 120 + 5);
if (tinfo.getTask().getLevel() <= levelLastAddedTask && maxYCoor != 5) {
maxYCoor += 95;
}
levelLastAddedTask = tinfo.getTask().getLevel();
taskicon.setLayoutY(maxYCoor);
iconPane.getChildren().add(taskicon);
for (int count = 0; count < tinfo.getChildren().size(); count++) {
createChildIcon(tinfo.getChildren().get(count));
}
}
JDK: 7, but it can be compiled as older versions as well
JFX: 2.2.21
After some testing with one of my colleagues we discovered that it must be a Windows issue. It works perfectly on Linux, but not on Windows.
Try adding VM option "-Dprism.order=j2d". This is a work-around rather than a solution because it is replacing your hardware accelerated graphic pipeline with a software pipeline. If this works, add VM option "-Dprism.verbose=true" so that we can see what hardware was problematic.
I added a bug in the JavaFX Issue Tracker: https://javafx-jira.kenai.com/browse/RT-31044.

iAds in a Scrolling View

In my application I want to show iAd in table view controller which have both navigation bar and Tab bar. I am able to show iAd in my application but this iAd is give trouble when I try to scroll, the problem is that iAd is also scrolling with the cells due to which I am not able to view the cell in bottom. I am creating the iAd using below code. can some one help me out in resolving following issue.
#pragma mark -
#pragma mark === Banner View Methods ===
#pragma mark -
- (void)createBannerView {
Class cls = NSClassFromString(#"ADBannerView");
if (cls) {
ADBannerView *adView = [[cls alloc] initWithFrame:CGRectZero];
adView.requiredContentSizeIdentifiers = [NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait,
ADBannerContentSizeIdentifierLandscape, nil];
// Set the current size based on device orientation
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
adView.delegate = self;
adView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
// Set intital frame to be offscreen
CGRect bannerFrame =adView.frame;
bannerFrame.origin.y = self.view.frame.size.height;
adView.frame = bannerFrame;
self.bannerView = adView;
[self.view addSubview:adView];
[adView release];
}
}
- (void)showBanner {
CGFloat fullViewHeight = self.view.frame.size.height;
CGRect tableFrame = self.tv.frame;
CGRect bannerFrame = self.bannerView.frame;
// Shrink the tableview to create space for banner
tableFrame.size.height = fullViewHeight - bannerFrame.size.height;
// Move banner onscreen
bannerFrame.origin.y = fullViewHeight - bannerFrame.size.height;
[UIView beginAnimations:#"showBanner" context:NULL];
self.tv.frame = tableFrame;
self.bannerView.frame = bannerFrame;
[UIView commitAnimations];
}
- (void)hideBanner {
// Grow the tableview to occupy space left by banner
CGFloat fullViewHeight = self.view.frame.size.height;
CGRect tableFrame = self.tv.frame;
tableFrame.size.height = fullViewHeight;
// Move the banner view offscreen
CGRect bannerFrame = self.bannerView.frame;
bannerFrame.origin.y = fullViewHeight;
self.tv.frame = tableFrame;
self.bannerView.frame = bannerFrame;
}
- (void)releaseBanner {
if (self.bannerView) {
bannerView.delegate = nil;
self.bannerView = nil;
}
}
- (void)changeBannerOrientation:(UIInterfaceOrientation)toOrientation {
if (UIInterfaceOrientationIsLandscape(toOrientation)) {
self.bannerView.currentContentSizeIdentifier =
ADBannerContentSizeIdentifierLandscape;
}
else {
self.bannerView.currentContentSizeIdentifier =
ADBannerContentSizeIdentifierPortrait;
}
}
#pragma mark -
#pragma mark === ADBannerViewDelegate Methods ===
#pragma mark -
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
[self showBanner];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[self hideBanner];
}
yes i got it you use addSubView which i think is wrong Add this Banner in last cell of a tableview or in a fotter of a table view if you are not use load more functionalty in you app
– tableView:viewForFooterInSection:
use that delegate method of table view UItableViewDelegate i think it helps you
– tableView:heightForFooterInSection:
add that method too to specify the height of footer of your table
add the calculate banner size code in
– tableView:heightForFooterInSection:
and add banner in – tableView:heightForFooterInSection:

Resources