How to set width of XIB-View to displaywidth - ios

I have my own xib-file looking like this:
Now I programmatically add multiple instances of this XIB-View to my original View which is a UIStackView with full width inside of a UIScrollView which takes the whole screen size.
I want to achieve, that every instance of my xib-view has the whole displayWidth. This is what I tried:
myView.frame.size = CGSize(myView.frame.size.height, view.frame.size.width())
But I can't change the view's width with this code. How do I achieve to change the width?
Edit: This is how I add the views (it is in java because I am using multi-os-engine but It is almost exactly like in objective-c)
#Override
#Selector("viewDidLoad")
public void viewDidLoad() {
UIView checkBox = instantiateFromNib("CheckBoxView");
for (UIView v : checkBox.subviews()) {
if (v instanceof DLRadioButton) {
((DLRadioButton) v).setTitleForState(option.getName(), UIControlState.Normal);
}
}
configView.addArrangedSubview(checkBox);
}
private UIView instantiateFromNib(String name) {
return (UIView) UINib.nibWithNibNameBundle(name, null).instantiateWithOwnerOptions(null, null).firstObject();
}

Related

How can an effect find out the display size of the UIImageView it is attached to?

In Xamarin.Forms, an Effect can be attached to a View. In my case, the View is displaying an Image. And the effect is making a colored "glow" around the visible pixels of the image. XAML:
<Image Source="{Binding LogoImage}" ...>
<Image.Effects>
<effects:GlowEffect Radius="5" Color="White" />
</Image.Effects>
</Image>
The effect is implemented as a subclass of RoutingEffect:
public class GlowEffect : Xamarin.Forms.RoutingEffect
{
public GlowEffect() : base("Core.ImageGlowEffect")
{
}
...
}
On each platform, there is a PlatformEffect to implement the effect. For iOS:
using ...
[assembly: ExportEffect(typeof(Core.Effects.ImageGlowEffect), "ImageGlowEffect")]
namespace Core.Effects
{
public class ImageGlowEffect : PlatformEffect
{
protected override void OnAttached()
{
ApplyGlow();
}
protected override void OnElementPropertyChanged( PropertyChangedEventArgs e )
{
base.OnElementPropertyChanged( e );
if (e.PropertyName == "Source") {
ApplyGlow();
}
}
private void ApplyGlow()
{
var imageView = Control as UIImageView;
if (imageView.Image == null)
return;
var effect = (GlowEffect)Element.Effects.FirstOrDefault(e => e is GlowEffect);
if (effect != null) {
CGRect outSize = AVFoundation.AVUtilities.WithAspectRatio( new CGRect( new CGPoint(), imageView.Image.Size ), imageView.Frame.Size );
...
}
}
...
}
}
The above code works if Source is changed dynamically: at the time Source changes, the UIImageView (Bounds or Frame) has a size. BUT if the Source is set statically in XAML, that logic does not run: so the only call to ApplyGlow is during OnAttach. UNFORTUNATELY, during OnAttach, the UIImageView has size (0, 0).
How get this effect to work on iOS, with a static Source?
NOTE: The equivalent Android effect works via a handler attached to ImageView.ViewTreeObserver.PreDraw - at which time the size is known. So if there is an iOS equivalent event, that would be one way to solve.
More Details:
The original implementation used the original image size (imageView.Image.Size) - which is available during OnAttach. This can be made to "work", but is not satisfactory: the glow is applied to the full size image. If the image is significantly larger than the view area, the glow becomes much too small a radius (iOS shrinks the image+glow as it renders): it does not have the desired appearance.
ApplyGlow has an option to apply a tint color to the image. That tint color is different than the glow color. I mention this because it restricts the possible solutions: AFAIK, can't just set options on an image and let iOS figure out how to render it - need to explicitly resize the image and draw the resized tinted image on top of a blurred version (the glow). This code all works - if imageView.Bounds.Size (or imageView.Frame.Size) is available (and non-zero).
With a breakpoint in OnElementPropertyChanged, I've checked to see if imageView size is known for any property that is always set. No; if no properties are dynamically set, the property changes all occur before imageView has a size.
Maybe it's a workaround and I don't know if it is acceptable to you.
Add a little delay before calling ApplyGlow(); in OnAttached. After the delay, you will get the imageView.Frame.Size or imageView.Bounds.Size.
protected override async void OnAttached()
{
await Task.Delay(TimeSpan.FromSeconds(0.3));// it can be 0.2s,0.1s, depending on you
ApplyGlow();
}
And if you have set WidthRequest, HeightRequest, you can get there without the delay:
private void ApplyGlow()
{
var imageView = Control as UIImageView;
if (imageView.Image == null)
return;
Console.WriteLine(imageView.Image.Size.Width);
Console.WriteLine(imageView.Image.Size.Height);
CoreGraphics.CGSize rect = imageView.Bounds.Size;
CoreGraphics.CGSize rect2 = imageView.Frame.Size;
Console.WriteLine(rect);
Console.WriteLine(rect2);
double width = (double)Element.GetValue(VisualElement.WidthRequestProperty);
double height = (double)Element.GetValue(VisualElement.HeightRequestProperty);
Console.WriteLine(width);
Console.WriteLine(height);
double width2 = (double)Element.GetValue(VisualElement.WidthProperty);
double height2 = (double)Element.GetValue(VisualElement.HeightProperty);
Console.WriteLine(width2);
Console.WriteLine(height2);
}

Xamarin iOS subview created inside custom UIView class not showing up

I have a custom UIView class, where I would like to add a bunch of subviews. This should be a simple task but I can't seem to figure out why my views are not showing up on the UI.
The examples and tutorials out there are pretty straight forward and I believe I have implemented my code correctly. I have added a breakpoint at AddSubview and the container has a size.
The UIViewController class already has a background image added it via the storyboard.
Can someone help me see what I'm probably missing? I know it's something small.
Below is my code:
The custom view class
public class RotaryWheel : UIView
{
int numberOfSections;
public RotaryWheel(CGRect frame,int sections): base(frame)
{
numberOfSections = sections;
DrawWheel();
}
public void DrawWheel()
{
// derive the center x and y
float centerX = (float)(Frame.Width / 2);
float centerY = (float)(Frame.Height / 2);
container = new UIView();
container.Frame = new RectangleF(centerX, centerY, 100, 100);
container.BackgroundColor = UIColor.White;
AddSubview(container);
}
}
The UIViewController class where I initialize the view
public partial class HomePageController : UIViewController
{
public override void LoadView()
{
base.LoadView();
rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
}
First at all, refer to loadView(). As the documentation mentioned
If you want to perform any additional initialization of your views, do so in the viewDidLoad() method.
We should never initialize subviews in LoadView, so I try to move that code to viewDidLoad
public override void ViewDidLoad()
{
base.ViewDidLoad();
RotaryWheel rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
But it looks like this, the subviews doesn't locate as expected.
After moving that code to ViewDidAppear, the problem disappeared.
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if(rotaryWheel == null){
rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
}
Summary:
The real size of View comes out in method ViewDidAppear. If you don't use autoLayout , you should manage the View Frame carefully.
And about why you didn't see the subview, I guess you selected the iphone4 ,iPhone 5 or iPhone5S simulator for test , the screen width is 320, you create RotaryWheel with X= 20 , centerX is 300 in method LoadView, so it is displayed out of screen.

Why does sizeThatFits() return a size that is too small?

I'm learning swift with cs193p and I have a problem with UITextView.sizeThatFits(...). It should return a recommended size for popover view to display an [int] array as a text. As you can see in Paul Hegarty's example (https://youtu.be/gjl2gc70YHM?t=1h43m17s), he gets perfectly-fit popover window without scrollbar. I'm using almost the same code that was in this lecture, but instead i've got this:
the text string equals [100], but the sizeThatFits() method is returning a size that is too small to display it nicely, even though there is plenty of free space.
It is getting a bit better after I've added some text, but still not precise and with the scrollbar:
Here is the part of the code where the size is being set:
override var preferredContentSize: CGSize {
get {
if textView != nil && presentingViewController != nil {
// I've added these outputs so I can see the exact numbers to try to understand how this works
print("presentingViewController!.view.bounds.size = \(presentingViewController!.view.bounds.size)")
print("sizeThatFits = \(textView.sizeThatFits(presentingViewController!.view.bounds.size))")
return textView.sizeThatFits(presentingViewController!.view.bounds.size)
} else { return super.preferredContentSize }
}
set { super.preferredContentSize = newValue }
}
What should I do so this will work in the same way as in the lecture?
It looks like there are 16 pt margins between the label and its parent view. You need to take that into account when returning the preferred size of the popover.
You should try both of the following:
Add 32 to the width that's returned from preferredContentSize
In Interface Builder, clear the layout constraints on your UILabel, then re-add top, bottom, leading, and trailing constraints and make sure that "Constrain to Margins" option is not enabled.
Finally, instead of overriding preferredContentSize, you can simply set the preferredContentSize when your view is ready to display, and you can ask Auto Layout to choose the best size:
override func viewDidLayoutSubviews() {
self.preferredContentSize = self.view.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
}
If your layout is configured correctly, systemLayoutSizeFitting(UILayoutFittingCompressedSize) will return the smallest possible size for your view, taking into account all of the margins and sub-views.

Add a UIView to a ScrollView for paging

How can I have a UIScollView content be the content from a UIView? I want to design the app layout in a UIView and then lay that view into a UIScrollView that is connected to a UIPageControl for pagination. So when the user swipes to the side, the next view is displayed. I have a sort of idea on how I would accomplish this, but I want to get it right without wasting a lot of time.
Heres my DetailViewController:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using UIKit;
using Foundation;
using CoreGraphics;
using CloudKit;
namespace RecordStorePro
{
public partial class DetailViewController : UIViewController
{
public Record DetailRecord { get; set; }
public DetailViewController (IntPtr handle) : base (handle)
{
}
public void SetDetailRecord (Record record)
{
if (DetailRecord != record) {
DetailRecord = record;
// Update the view
ConfigureView ();
}
}
void ConfigureView ()
{
// Update the user interface for the detail item
if (IsViewLoaded && DetailRecord != null) {
//label.Text = DetailRecord.Album;
}
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
NavigationItem.SetLeftBarButtonItem (new UIBarButtonItem(UIBarButtonSystemItem.Stop, (sender, args) => {
NavigationController.PopViewController(true);
}), true);
ConfigureView ();
//this.scrollview.BackgroundColor = UIColor.Gray;
// set pages and content size
scrollview.ContentSize = new SizeF ((float)(scrollview.Frame.Width * 2), (float)(scrollview.Frame.Height));
//this.scrollview.AddSubview ();
this.scrollview.Scrolled += ScrollEvent;
}
private void ScrollEvent(object sender, EventArgs e)
{
this.pagecontrol.CurrentPage = (int)System.Math.Floor(scrollview.ContentOffset.X / this.scrollview.Frame.Size.Width);
}
public override void DidReceiveMemoryWarning ()
{
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
}
}
So when I swipe the screen, I want a subview that contains some labels and textfields to come in and replace the original labels and textfields. It works properly so far except I can't figure out how to add the subview, and make it size appropriately to different screen sizes.
EDIT
Heres the problem Im facing now, the views laying in the ScrollView act funny and are about 44f too tall so they let me drag up and down. i tried setting all kinds of constraints as well as manually setting them to -44 smaller with no help. heres a picture of the problem now:
Heres the screenshot of my constraints set.
View A:
View B:
ScrollView:
Nib view:
To do this, you might want to try these steps:
The view that shows the first page will be called View A. The view that shows the second page will be called View B.
Add both views to the Scroll View.
Control-drag from View A to the Scroll View in the sidebar.
Hold down Shift and select Top Space to Superview, Bottom Space to Superview, and Leading Space to Superview. Next, press Return to add those constraints.
Make sure those constraints’ constants are set to 0.
Control-drag from View B to the Scroll View in the sidebar.
Hold down Shift and select Top Space to Superview, Bottom Space to Superview, and Trailing Space to Superview. Next, press Return to add those constraints.
Control-drag from View A to View B in the sidebar.
Select Horizontal Spacing. Make sure its constant is 0 and its Second Item is View A.Trailing and its First Item is View B.Leading.
Control-drag from View A to View B in the sidebar. Select Equal Widths. Make sure the Constant is 0.
Control-drag from View A to the Scroll View in the sidebar. Select Equal Widths. Make sure the constant is set to 0.
In the inspector, check “Paging Enabled.”
Adding subScrollView with no pagingEnabled to each page and controls into subScrollView works! If you add controls directly to scrollview with paging enabled, it seems gesture recogniser's first responder is always scrollview so your controls never gets the event and behave like a disabled!
UIScrollView has a property called “pagingEnabled”
In Interface Builder, resize the scroll view to allow for some space below it for the page control. Next, drag in a UIPageControl from the library, centered below the scroll view. Resize the UIPageControl to take up the full width of the view.

Set NSLayoutConstraint Constant in iOS7

Ok, I have a super weird issue. I have a UIScrollView that I'm using to update the position of a second UIView. So, when you scroll the UIScrollView, the UIView moves as well at a proportional but slower speed. The code is pretty simple.
Scroller.Scrolled += delegate {
if (Scroller.ContentOffset.Y <= 0) {
filtersTop.Constant = 0;
} else {
filtersTop.Constant = -(Scroller.ContentOffset.Y / 2);
}
}
filtersTop is a NSLayoutConstraint. Scroller is the UIScrollView.
The problem is whenever I set filtersTop.Constant to any value derived from Scroller.Content.Y, it resets Scroll.ContentOffset to 0 in iOS7 only. This code works fine in iOS8+. I've tried a dozen variations on this. If I set filtersTop.Constant to a static number (ie: 123.5), that works.
I've tried saving the value to a variable and forcing the type thinking maybe it was getting cast improperly. If I trace out the value, it works. But, the minute I set it to filtersTop.Constant, it resets Scroller.ConstentOffset.Y again. Scroller and filters are NOT related. They are not nested or associated with each other in any way. So, I have no idea why setting the constant on the constraint on filters would in any way affect Scroller.
Anyone know what is happening here?
Ok I had another look it seems to be resetting the contentSize between calling ViewWillLayoutSubviews and calling ViewDidLayoutSubviews
ContentSize set to: {Width=320, Height=3000}
Called ViewWillLayoutSubviews
ContentSize set to: {Width=320, Height=3000}
ContentSize set to: {Width=0, Height=0}
Called ViewDidLayoutSubviews
I then subclassed the scrollview and forced the contentSize to stay the same like so:
public class CustomScroller : UIScrollView
{
public CustomScroller ()
{
ContentSize = new CGSize(320,3000);
}
public override CGSize ContentSize {
get {
return base.ContentSize;//new CGSize(320,3000);
}
set {
base.ContentSize = new CGSize(320,3000);
Console.WriteLine ("ContentSize set to: "+ContentSize);
}
}
public override void LayoutSubviews ()
{
Console.WriteLine ("LayoutSubviews: "+Frame.Y);
base.LayoutSubviews ();
}
}
You will then have to set the contentSize when the view loads rather than in ViewDidLayoutSubviews.
I have updated the code here with the solution detailed above and here is the result.
Note I changed the scrolled code but it will work with the original too.
This works in iOS7 and iOS8. Hope this helps!

Resources