How can I implement a box shadow element in swift? - ios

Please see the box attached. I want to implement that box in swift. How can I do it?
Here's the CSS:
background: #FFFFFF;
border: 1px solid #DAD9D9;
box-shadow: inset 2px 2px 0px rgba(0, 0, 0, 0.25);
border-radius: 30px 30px 0px 0px;
Here's what I tried:
self.answerView.layer.shadowColor = UIColor(red: 0.00, green: 0.00, blue: 0.00, alpha: 1.0).cgColor
self.answerView.layer.shadowOpacity = 0.25
self.answerView.layer.cornerRadius = 30
self.answerView.layer.shadowOffset = CGSize(width: 2, height: 2);
self.answerView.layer.shadowRadius = 0

Try this:
self.answerView.borderStyle = .none
self.answerView.layer.cornerRadius = 10
self.answerView.layer.backgroundColor = UIColor.systemGray3.cgColor
self.answerView.backgroundColor = UIColor.systemBackground
self.answerView.layer.shadowColor = UIColor.systemGray3.cgColor
self.answerView.layer.shadowOpacity = 1
self.answerView.layer.shadowRadius = 0
self.answerView.layer.shadowOffset = CGSize(width: 0, height: 2.5);
self.answerView.layer.shadowRadius = 0
self.answerView.borderColor = UIColor.systemGray3
self.answerView.borderWidth = 2

If you only want to round the top corners, you should set the appropriate values to CALayer.maskedCorners.

Related

How can I animate a UIView's rotation only within a certain degree range?

In my app, I've got a simulation of an analog readout dial, like you might find on a car dashboard or a control panel. Run the following snippet for an example of what I mean:
svg { height: 100vh; }
#dial {
fill: white;
stroke: #CCCCCC;
stroke-dasharray: 405;
transform: rotate(135deg);
transform-origin: center;
}
#pointer {
fill: #FF0000;
animation: 2s infinite alternate dialrotate;
transform-origin: bottom center;
animation-timing-function: ease-in-out;
}
#keyframes dialrotate {
from {
transform: rotate(-135deg);
}
to {
transform: rotate(135deg);
}
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<circle id="dial" cx="99.5" cy="100.5" r="86"/>
<polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>
(I'm not using SVGs in my app--I just threw the example together using an SVG because I'm primarily a web developer)
Here's how I'm animating the dial movement right now:
UIView.animate(
withDuration: 0.1,
delay: 0.0,
options: .curveEaseOut,
animations: {
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
},
completion: nil
)
I want to animate the pointer's direction between the two extremes (270 degrees). But I DON'T want the pointer to rotate across the shorter 90 degree path outside the dial's bounds; i.e. this is WRONG:
svg { height: 100vh; }
#dial {
fill: white;
stroke: #CCCCCC;
stroke-dasharray: 405;
transform: rotate(135deg);
transform-origin: center;
}
#pointer {
fill: #FF0000;
animation: 2s infinite alternate dialrotate;
transform-origin: bottom center;
animation-timing-function: ease-in-out;
}
#keyframes dialrotate {
from {
transform: rotate(225deg);
}
to {
transform: rotate(135deg);
}
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<circle id="dial" cx="99.5" cy="100.5" r="86"/>
<polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>
How can I force the UIView's rotation to rotate the long way around, instead of rotating over the shortest direction?
As you have discovered, by default UIView animation takes the shortest path when interpolating angle values. You have a couple of options:
You can decompose the animation into several shorter steps, or keyframe it.
You can use a CABasicAnimation instead and animate your layer's transform using a cumulative animation
Playground example:
import PlaygroundSupport
import UIKit
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let view = UIView(frame: rect)
let bar = UIView(frame: rect.insetBy(dx: 40, dy: 140))
bar.backgroundColor = .red
view.addSubview(bar)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.toValue = 1.25 * CGFloat.pi
animation.isCumulative = true
animation.duration = 5
bar.layer.add(animation, forKey: "spin")
bar.layer.transform = CATransform3DMakeRotation(1.25 * CGFloat.pi, 0, 0, 1)
PlaygroundPage.current.liveView = view
You can do it like this
UIView.animate(
withDuration: 0.1,
delay: 0.0,
options: .curveEaseOut,
animations: {
if radians > CGFloat.pi {
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi);
}
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
},
completion: nil
)

Konvajs Clipping function but with opacity

I've been using the Konvajs clipping function to limit what can be seen inside a bounding box.
Has anyone managed to write a similar function but when the konva object leaves the bounding box it changes in opacity instead of simply not being seen?
Thanks for the input.
Here's a working version. The yellow region is the canvas extent. The clear image in the center is the clipping region. The semi-transparent region is the full extent of the image. Click & drag the clipped region and you can see the 'clipped off' component of the image displayed.
// this is the position for the clipping rectangle
var clipRect = {left: 30, top: 30, width : 420, height : 340, right: 450, bottom: 390};
// generic
// add a stage & add a layer
var s = new Konva.Stage({container: 'container', width: 800, height: 600});
var l = new Konva.Layer({draggable: true});
// background layer
var bgr = new Konva.Rect({x:0, y: 0, width: 500, height: 500, fill: 'gold', opacity: 0.1, listening: false})
l.add(bgr)
s.add(l);
// end of generic
// Add an image to show the full extent of the clipped image
var boundsRect = new Konva.Image({opacity: 0.2, stroke: 'black', strokeWidth: 1, draggable: false, dash: [2, 2], listening: false});
l.add(boundsRect);
// to clip we have to add a group with a clip.
var vp = new Konva.Group({
clip: { x: clipRect.left, y: clipRect.top, width : clipRect.width, height : clipRect.height}
});
// add a border to the clip region via a rect just surrounding it.
var r1 = new Konva.Rect({x: clipRect.left - 1, y: clipRect.top - 1, width : clipRect.width + 2, height : clipRect.height + 2, stroke: '#00008B', strokeWidth: 1, draggable: false});
l.add(r1);
/* This is the image that is the subject of the clipping effort */
var i=new Konva.Image({x: 0, y: 0, width: 0, height: 0, draggable: true,
// we want to ensure that the image cannot be dragged
// beyond the glip rect bounds.
dragBoundFunc: function(pos) {
var posRect = getPosRect(pos,this);
var iPos = this.getClientRect();
var newX = pos.x;
var newY = pos.y;
newX = (posRect.left >= clipRect.left) ? clipRect.left : posRect.left;
newX = (posRect.right <= clipRect.right) ? clipRect.right - posRect.width : newX;
newY = (posRect.top >= clipRect.top) ? clipRect.top : posRect.top;
newY = (posRect.bottom <= clipRect.bottom) ? clipRect.bottom - posRect.height : newY;
return {
x: newX,
y: newY
}}
});
i.on('dragmove', function() {
setBoundRect(this);
});
vp.add(i);
var imageObj = new Image();
imageObj.onload=function () {
i.image(imageObj);
boundsRect.image(imageObj);
i.width(imageObj.width);
i.height(imageObj.height);
setBoundRect(i);
s.draw();
}
imageObj.src = "http://btckstorage.blob.core.windows.net/site13706/gallery/2015/Porthleven%20lifeboat%20day%202015%20f%20-%20Chris%20Yacoubian%20Ltd.jpg";
l.add(vp) // add image to clipping viewport
s.draw()
// used to get the client rect of a shape.
function getPosRect(pos, ele){
var cliPos = ele.getClientRect();
var posRect = {left: pos.x, top: pos.y, right: pos.x + cliPos.width, bottom: pos.y + cliPos.height, width: cliPos.width, height: cliPos.height};
return posRect;
}
// set the bounds rect to the size of the given element
function setBoundRect(ele){
var x = ele.position();
var posRect = getPosRect(ele.position(), ele);
boundsRect.position({x: posRect.left, y: posRect.top});
boundsRect.size({width: posRect.width, height: posRect.height});
}
// change the image
$('img').on("click", function(e){
imageObj.src = $(this).prop("src");
})
.pic
{
max-width:150px;
max-height: 80px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<div id='container' style="display: inline-block; width: 750px; height: 400px; background-color: transparent; overflow: hidden;"></div>
<div style="display: inline-block; width: 500px; height: 400px; " >
<p>
<img class='pic' src="http://btckstorage.blob.core.windows.net/site13706/gallery/2015/53%20-%20Chris%20Yacoubian%20Ltd.jpg"/>
</p>
<p>
<img class='pic'src="http://btckstorage.blob.core.windows.net/site13706/gallery/2015/Crew%20day%20Mousehole%202015%20e%20-%20Chris%20Yacoubian%20Ltd.jpg" />
</p>
<p>
<img class='pic' src="http://btckstorage.blob.core.windows.net/site13706/gallery/2015/34%20-%20Chris%20Yacoubian%20Ltd.jpg"/>
</p>

Split a circular UIView into 5 equal angle pieces

I have a UIView created programmatically like that:
forecastWeatherWheel.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.1)
forecastWeatherWheel.frame = CGRect(x: -(dailyWeatherContainer.frame.width + 100)/2,
y: self.view.bounds.height/2 - ((dailyWeatherContainer.frame.height + 100)/2),
width: dailyWeatherContainer.frame.width + 100,
height: dailyWeatherContainer.frame.height + 100)
forecastWeatherWheel.layer.cornerRadius = forecastWeatherWheel.bounds.size.width/2
forecastWeatherWheel.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(forecastWeatherWheel)
I need to add (again programmatically) 5 subViews to this UIView.
I'm struggling in finding the coordinates for the anchor point of the subViews.
Thinking into Degrees, my circled superView will have to be split into 72° each equal pieces and the coordinates of the border will have to be the anchor point of my subViews.
Something like this:
Like this (starting at the top and going clockwise):
let radius: Double = 100
let cx: Double = 0
let cy: Double = 0
for deg in stride(from: 90, to: -269, by: -72) {
let a = Double(deg)*M_PI/180
let x = cx + radius * cos(a)
let y = cy + radius * sin(a)
print("Angle \(a): \(x), \(y)")
}
If anyone looking for js scripts.
let radius = 460/2;
let cx = radius;
let cy = radius;
$(".ar__creatCircle .ar__each_circles").each(function(i){
let a = 72*i *Math.PI/180
let x = cx + radius * Math.cos(a);
let y = cy + radius * Math.sin(a);
$(this).css({
left: x+"px",
bottom: y+"px",
})
});
.ar__creatCircle {
width: 460px;
height: 460px;
border-radius: 50%;
border: 1px dashed #000;
/* -webkit-transition: all cubic-bezier(0.35, 0.84, 0.57, 1.04) 300ms;
transition: all cubic-bezier(0.35, 0.84, 0.57, 1.04) 300ms; */
z-index: 9;
}
.ar__each_circles {
width: 30px;
height: 30px;
position: absolute;
border-radius: 50%;
background: #54c970;
position: absolute;
z-index: 2;
min-width: 30px;
min-height: 30px;
transform: translate(-50%, 50%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="ar__creatCircle" style="transform: rotate(0deg);">
<div class="ar_circle_innr" >
<div class="ar__each_circles">
</div>
<div class="ar__each_circles">
</div>
<div class="ar__each_circles" >
</div>
<div class="ar__each_circles" >
</div>
<div class="ar__each_circles" >
</div>
</div>
</div>

Gradients give the wrong color on iOS mobile safari

I'm developing a mobile app with Cordova Phonegap that uses gradients that stack on top of eachother. On Android everything works as it is supposed to but on iOS the gradients shows up different. The edges are green whereas when I preview it in my browser it is blue as it's supposed to be. There's also this weird transition at the bottom of the page.
This is my css:
#gradient2Layer1 {
position: fixed;
height: 100px;
bottom: 0;
left: 0;
height: 20%;
width: 100%;
color = "blue";
background: -webkit-linear-gradient(270deg, rgba(15,431,28,0) 35%, #b3c6ff 50%,rgb(128,128,128) 100%);
z-index: 100; }
#gradient2Layer2 {
position: fixed;
height: 100px;
bottom: 0;
left: 0;
height: 20%;
width: 100%;
opacity: 0.5;
color = "blue";
background: -webkit-linear-gradient(270deg, rgba(15,431,28,0) 35%, blue 50%, blue 100%);
animation: fadeIn 5s infinite alternate;
z-index: 100; }
How can I fix this?
I really believe this is a typo and you wanted to use rgba(15,43,128,0) (which is the shade of blue you're looking for) instead of rgba(15,431,28,0), which is not even a valid RGB value (limited to 0..255).

Possible to have a fully transparent inset box-shadow?

Is it possible to have the two divs with box-shadow overlap with fully transparent edges? In my example, I want to keep the faded, kind of rounded edge, but it's important that the underlying box is visible through the fade. But as you can see the faded edge is not entirely transparent so it will show a white border rather than let the blue color shine through..
Is it possible to make this work without resorting to png or similar?
Example
.bg {
background-image: -moz-linear-gradient(right top , rgba(255, 255, 255, 0) 0%, #FF0000 100%);
box-shadow: 0 0 90px 90px rgba(255, 255, 255, 0) inset;
-webkit-box-shadow: 0 0 90px 90px rgba(255, 255, 255, 0) inset;
-moz-box-shadow: 0 0 90px 90px rgba(255, 255, 255, 0) inset;
opacity: 0.7;
position: absolute;
}
I don't know whether this is possible with inset box-shadow; however you can make them overlap seamlessly with outset box-shadow for the overlapping element.
All you have to do is give the overlapping element a box-shadow color that is the same as its background-color.
I have edited your sample here: (I didn't copy all the vendor-specific prefixes, just used box-shadow).
http://jsbin.com/orajot/4/edit

Resources