Setting size of JTCalendar cell - ios

I am working through the tutorial of JTCalendar (version 6.1.5). When I run on smaller phones such as the iPhone SE, one side of the circle in the Selection View gets clipped. This because the cells are about 45x45 points, but the Selection View's size is 50x50 points and is therefore too large to fully fit into the cell.
How can I make my selection view fit properly into date cells of varying sizes?
How can I get the proper value of cornerRadius for the selection view circle?

I was able to resolve this issue. The problem is that the tutorial set the size of the Selected View and left it at that. What I did was
Made Outlets for both the width and height constraints in CellView.swift
In ViewContoller.swift, I modified cell selection as follows:
if cellState.isSelected {
var parentMinDimension = min(view.frame.width, view.frame.height)
parentMinDimension = round(parentMinDimension - 0.5)
myCustomCell.widthConstraint.constant = parentMinDimension
myCustomCell.heightConstraint.constant = parentMinDimension
myCustomCell.selectedView.layer.cornerRadius = parentMinDimension / 2
myCustomCell.selectedView.isHidden = false
} else {
myCustomCell.selectedView.isHidden = true
}
This gets the parent view and determines the smaller dimension. This needs to be rounded down. I then uses this parent view dimension to set the width and height of the CellView as well as for determining the corner radius.

Related

UIScrollView with dynamically sized content

(Xcode 11, Swift)
Being a newbie to iOS and Autolayout, I'm struggling with implementing a fairly simple (IMHO) view which displays a [vertical] list of items. The only problem is that items are decided dynamically and each of them could be either text or image (where either of those could be fairly large so scrolling would be required). WebView is not an option, so it has to be implemented natively.
This is how I understand the process:
Make in IB a UIScrollView and size it to the size of the outer frame.
Make a container view as a subview of UIScrollView (again, in IB) and size it the same.
Set constraint on equal width of both
At runtime, populate container view with UILabels/UIImageViews and also set constraints programmatically to ensure proper layout.
"Tell" scrollview about the subview height in order to make it manage the scrolling thereof.
Is this the right approach? It doesn't seem to work for me (for a toy example of dynamically adding a very tall image to a container view - I cannot get the scrolling to work). What would be the proper way to do the last step in the process above - just force the contentSize of the scrollview to the size of the populated container view (it doesn't seem to work for me). Any help would be appreciated.
When adding multiple elements to a scroll view at run-time, you may find it much easier to use a UIStackView... when setup properly, it will automatically grow in height with each added object.
As a simple example...
1) Start by adding a UIScrollView (I gave it a blue background to make it easier to see). Constrain it to Zero on all 4 sides:
Note that we see the "red circle" indicating missing / conflicting constraints. Ignore that for now.
2) Add a UIView as a "content view" to the scroll view (I gave it a systemYellow background to make it easier to see). Constrain it to Zero on all 4 sides to the Content Layout Guide -- this will (eventually) define the scroll view's content size. Also constrain it equal width and equal height to the Frame Layout Guide:
Important Step: Select the Height constraint, and in the Size Inspector pane select the Placeholder - Remove at build time checkbox. This will satisfy auto-layout in IB during design time, but will allow the height of that view to shrink / grow as necessary.
3) Add a Vertical UIStackView to the "content view". Constrain it to Zero on all 4 sides. Configure its properties to Fill / Fill / 8 (as shown below):
4) Add an #IBOutlet connection to the stack view in your view controller class. Now, at run-time, as you add UI elements to the stack view, all of your "scrollability" will be handled by auto-layout.
Here is an example class:
class DynaScrollViewController: UIViewController {
#IBOutlet var theStackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// local var so we can reuse it
var theLabel = UILabel()
var theImageView = UIImageView()
// create a new label
theLabel = UILabel()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theLabel.translatesAutoresizingMaskIntoConstraints = false
// multi-line
theLabel.numberOfLines = 0
// cyan background to make it easy to see
theLabel.backgroundColor = .cyan
// add 9 lines of text to the label
theLabel.text = (1...9).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .yellow
// add 5 lines of text to the label
theLabel.text = (1...5).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// create a new UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load an image for it - I have one named background
if let img = UIImage(named: "background") {
theImageView.image = img
}
// let's give the image view a 4:3 width:height ratio
theImageView.widthAnchor.constraint(equalTo: theImageView.heightAnchor, multiplier: 4.0/3.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
// add another label
theLabel = UILabel()
// multi-line
theLabel.numberOfLines = 0
// yellow background to make it easy to see
theLabel.backgroundColor = .green
// add 2 lines of text to the label
theLabel.text = (1...2).map({ "Line \($0)" }).joined(separator: "\n")
// add it to the stack view
theStackView.addArrangedSubview(theLabel)
// add another UIImageView
theImageView = UIImageView()
// this gets set to false when the label is added to a stack view,
// but good to get in the habit of setting it
theImageView.translatesAutoresizingMaskIntoConstraints = false
// load a different image for it - I have one named AquariumBG
if let img = UIImage(named: "AquariumBG") {
theImageView.image = img
}
// let's give this image view a 1:1 width:height ratio
theImageView.heightAnchor.constraint(equalTo: theImageView.widthAnchor, multiplier: 1.0).isActive = true
// add it to the stack view
theStackView.addArrangedSubview(theImageView)
}
}
If the steps have been followed, you should get this output:
and, after scrolling to the bottom:
Alignment constraints (leading/trailing/top/bottom)
The alignment constraint between Scroll View and Content View defines the scrollable range of the content. For example,
If scrollView.bottom = contentView.bottom, it means Scroll View is
scrollable to the bottom of Content View.
If scrollView.bottom = contentView.bottom + 100, the scrollable
bottom end of Scroll View will exceed the end of Content View by 100
points.
If scrollView.bottom = contentView.bottom — 100, the bottom of
Content View will not be reached even the scrollView is scrolled to
the bottom end.
That is, the (bottom) anchor on Scroll View indicates the (bottom) edge of the outer frame, i.e., the visible part of Content View; the (bottom) anchor on Content View refers to the edge of the actual content, which will be hidden if not scrolled to.
Unlike normal use cases, alignment constraints between Scroll View and Content View have nothing to do with the actual size of Content View. They affect only “scrollable range of content view” but NOT “actual content size”. The actual size of Content View must be additionally defined.
Size constraints (width/height)
To actually size Content View, we may set the size of Content View to a specific length, like width/height of 500. If the width/height exceeds the width/height of Scroll View, there will be a scrollbar for users to scroll.
However, a more common case will be, we want Content View to have the same width (or height) as Scroll View. In this case, we will have
contentView.width = scrollView.width
The width of Content View refers to the actual full width of content. On the other hand, the width of Scroll View refers to the outer container frame width of Scroll View. Of course, it doesn’t have to be the same width, but can be other forms like a * scrollView.width + b.
And if we have Content View higher (or wider) than Scroll View, a scrollbar appears.
Content View can not only be a single view, but also multiple views, as long as they are appropriately constrained using alignment and size constraints to Scroll View.
For details, you may follow this article: Link.

Rounded UIImageView

I'm trying to get a rounded UIImageView but it seems to render differently on different devices;
Looks like this on an iPhone Xr:
Looks like this on an iPhone 7:
I have a height constraint of 60 and the following code:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.userAvatar.layer.cornerRadius = self.userAvatar.frame.height / 2
self.userAvatar.layer.masksToBounds = false
self.userAvatar.clipsToBounds = true
self.userAvatar.layer.borderWidth = 0
}
Any ideas?
It seems to me that you have given the image leading and trailing constraints instead of a fixed width.
To achieve a circle give image view width equal to height.
This happens due to different widths of devices.
If you're managing this view using Interface Builder (i.e. Storyboard or XIB), you can enforce a square shape (which becomes a circle when combined with the rounded corners you already have) for the view directly from there by defining a constraint for its Aspect Ratio. No need to code anything.
Control-drag (like you do to create Outlets, Actions, etc.) from the image view to itself, and the following popup will appear.
Select Aspect Ratio, which will create a constraint matching whatever the view's current ratio is (in this example, it's 15:8). If the view was already square, the constraint created should already be correct.
If not, you can find that constraint by clicking the following icon (for the Size inspector):
From there, you can double-click on that constraint to edit it, and change the Multiplier to 1:1:
In fact, an even easier option is, once you've Control-dragged from the view to itself, hold down Alt/Opt and the option displayed in the popup will change to Aspect Ratio (1:1), meaning you can set it directly from there without even having to edit the constraint.
Constrain the height equal to the width.
And, create a simple UIImageView subclass:
class RoundedImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height / 2
}
}
The frame can (and will) change based on view lifecycle. By updating the cornerRadius in layoutSubviews() it will keep it "round".

iPhone - resizing image and two labels to fit the screen

I'm new to iPhone developing. I have a screen with image, title and content:
Image has dynamic size and also all text can have any length. What I want to achievie is to fit the image exactly in screen and make all labels to be only as long as they should be.
Afterk about 5 hours of work I have:
Working labels length, achieved by setting Lines=0 and Preferred Width=content width
Not working image, which lefts blank spaces
I have read a lot, I understand Content Hugging Priority and Content Compression Resistance Priority. I know differences in UIViewContentMode. I know that I can manually set UIImage.frame=CGRectMake(...). Nothing works. My image is more wider than higher (lets assume that width/height = 3). If I have UIViewContentMode=Aspect Fit then I will have blank space above and below the image. If set to Aspect fill then image is too high and also cropped. Changing frame in code doesn't change anything in view. What I want is to have image with size that was made using Aspect fit, but without blank space.
Finally I dit it. First of all, everyone writes that UIImageView.Frame should be modified to shrink the image, but be aware, that if your view is UITableViewCell then you should do that in layoutSubviews method (remember to invoke super.layoutSubviews() at the begging).
Secondly, make in IB a height constraint for your UIImageView for temporary value (for example 100). You will change that constraint in layoutSubviews method.
And finally: fitting image is NOT about its ratio. Set UIViewContentMode=Aspect Fit. Then the case with blank top and bottom areas occurs only when the initial image is wider than screen. Calculating the proper height should also take place in layoutSubviews.
Example implementation:
override func layoutSubviews() {
super.layoutSubviews()
var imageWidth = (imageViewObject.image?.size.width)!
var imageHeight = (imageViewObject.image?.size.height)!
var frameWidth = UIScreen.mainScreen().bounds.width
var properHeight: CGFloat
if imageWidth > frameWidth {
var ratio = frameWidth / imageWidth
properHeight = imageHeight * ratio
}
else {
properHeight = imageHeight
}
imageHeightConstraint.constant = properHeight
imageViewObject.frame = CGRectMake(0, 0, frameWidth, properHeight)
}

issues with UIImageView.layer.cornerRadius to create rounded images on different pixel densities ios

I'm simply trying to create a perfectly round image. Here's my swift code:
myImage.layer.cornerRadius = myImage.frame.size.width/2
myImage.layer.masksToBounds = true
This works on a 4s, but is not quite round on a 5s, and appears as a rounded rectangle on a iphone 6.
I'm assuming this has to do with frame.size.width returning values in pixels not points or something like that, but I've been unable to solve this problem.
If you're putting that code in viewDidLoad, try moving it to viewDidLayoutSubviews.
I'm guessing it's an auto layout issue -- although you've used the corner radius property appropriately and are in fact making the image frame circular, after auto-layout, the corner radius stays the same, but the image stretches so that it's no longer circular.
If your code is in viewDidLoad in ViewController, try moving it to viewDidLayoutSubviews.
If your rounded imageView is in tableViewCell, try moving it to draw.
override func draw(_ rect: CGRect) {
avatarView.layer.cornerRadius = avatarView.frame.size.width / 2
avatarView.layer.masksToBounds = true
avatarView.clipsToBounds = true
avatarView.contentMode = .scaleAspectFill
}
The problem is that if you change the cornerRadius of Any view, and expect it to look like a circle, the view has to be a square.
Now, because of different devices and different device size, the size of your image view might change and it may be a rectangle.
For e.g.
If you view is a 50 * 50
myImage.layer.cornerRadius = myImage.frame.size.width/2
This would add corner radios of 25 on both sides to make it a circle.
But because of auto layout of device change, your view might be a 50 * 80
Corner radius would add a 25, but as the height is 80, it will add 25 on both sides, and the remaining 30 won't be a curve and look straight.
What you can do is try viewing the screen in various orientations in the storyboard and change auto layout Constraints (Or structs and springs) to ensure that the image view is a square in all the devices
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myImage.layer.cornerRadius = myImage.frame.size.width/2
myImage.layer.masksToBounds = true
}
This should work:
myImage.layer.cornerRadius = myImage.**bounds**.size.width/2
myImage.layer.masksToBounds = true

How can I tap on a UICollectionView cell of a collection view in UIAutomation?

How can I tap on a UICollectionView cell of a collection view in UIAutomation?
I tried this
var iconsCollView = window.collectionViews()[0];
var iconRect = iconsCollView.cells()[0].rect();
var iconX = iconRect.origin.x/100;
var iconY = iconRect.origin.y/100;
iconsCollView.tapWithOptions({tapOffset:{x: iconX, y: iconY}});
but it taps another cell in the collection view, a wrong cell other than the cell I specified its offset.
Can you please help me? is there another way?
From the UIAElement class reference:
Your script should treat the rect object as a generic JavaScript object whose properties for origin, x, y, size, width, and height correspond to those of the analogous CGRect Cocoa structure. The rect object has the form {origin:{x:xposition,y:yposition}, size:{width:widthvalue, height:heightvalue}}. The relevant coordinates are screen-relative and are adjusted to account for device orientation.
From the same source under tapWithOptions method:
You can use offsets to achieve finer precision in specifying the hitpoint within the rect for the specified element. The offset comprises a pair of x and y values, each ranging from 0.0 to 1.0. These values represent, respectively, relative horizontal and vertical positions within the rect, with {x:0.0, y:0.0} as the top left and {x:1.0, y:1.0} as the bottom right. Thus, {x:0.3, y:0.6} specifies a position just below and to the left of center, and {x:1.0, y:0.5} specifies a position centered vertically at the far right.
From the source you provided, you're trying to tap inside of collection view passing some weirdly scaled offset coordinates of one of its cells (instead of expected relative horizontal and vertical positions within the rect of collection view).
If you want to tap the cell simply locate it and call the tap method on it:
var iconsCollView = window.collectionViews()[0];
var iconCellToTap = iconsCollView.cells()[0].tap();

Resources